web-dev-qa-db-ja.com

空のリストであるデフォルトのパラメーターを回避するPython的な方法は何ですか?

空のリストであるデフォルトのパラメータがあるのは自然なことです。まだ これらの状況ではPythonは予期しない動作をします

たとえば、関数がある場合:

def my_func(working_list = []):
    working_list.append("a")
    print(working_list)

最初に呼び出されたときはデフォルトが機能しますが、その後の呼び出しは既存のリストを更新し(呼び出しごとに「a」を1つ)、更新されたバージョンを出力します。

だから、私が望む行動を得るためのPythonの方法は何ですか(各呼び出しの新しいリスト)?

82
John Mulder
def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

ドキュメント デフォルトとしてNoneを使用し、明示的に テスト 関数の本体で使用する必要があると言います。

112
HenryR

既存の回答は、要求された直接的なソリューションをすでに提供しています。ただし、これは新しいPythonプログラマにとって非常に一般的な落とし穴であるため、pythonがこのように動作する理由を説明する価値があります。 The Hitchhikers Guide to Python "as"Mutable Default Arguments ":- http://docs.python-guide.org/en/latest/writing/gotchas/

引用:「Pythonのデフォルト引数は、関数が呼び出されるたびにではなく、関数が定義されるときに1回評価されます(たとえば、Rubyなど)。これは、可変デフォルト引数を使用し、それを変更すると、関数への今後のすべての呼び出しでもそのオブジェクトを変更します

実装するサンプルコード:

def foo(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to
18
Zhenhua

この場合重要ではありませんが、オブジェクトIDを使用してNoneをテストできます。

if working_list is None: working_list = []

ブール演算子またはPythonでの定義方法を利用することもできます。

working_list = working_list or []

ただし、呼び出し側がworking_listとして空のリスト(falseとしてカウント)を与え、関数が彼が与えたリストを変更することを期待する場合、これは予期しない動作をします。

11
bendin

関数の目的がmodify _working_list_として渡されるパラメーターである場合、HenryRの答えを参照してください(= None、内部にNoneがないか確認してください)。

ただし、引数を変更するつもりがない場合は、リストの開始点として使用するだけで、単純にコピーできます。

_def myFunc(starting_list = []):
    starting_list = list(starting_list)
    starting_list.append("a")
    print starting_list
_

(またはこの単純なケースでは_print starting_list + ["a"]_だけですが、それは単なるおもちゃの例だったと思います)

一般的に、引数の変更はPythonのスタイルとしては悪いです。オブジェクトを変更することが完全に期待される唯一の関数は、オブジェクトのメソッドです。オプションの引数を変更することはさらにまれです-いくつかの呼び出しでのみ発生する副作用は本当に最高のインターフェースですか?

  • 「出力引数」のCの習慣からそれを行う場合、それは完全に不要です-常に複数の値をTupleとして返すことができます。

  • 中間リストを作成せずに結果の長いリストを効率的に作成するためにこれを行う場合は、ジェネレーターとして作成し、呼び出し時にresult_list.extend(myFunc())を使用することを検討してください。このようにして、呼び出し規約は非常にきれいなままです。

オプションの引数isを頻繁に変更するパターンの1つは、再帰関数の隠された「メモ」引数です。

_def depth_first_walk_graph(graph, node, _visited=None):
    if _visited is None:
        _visited = set()  # create memo once in top-level call

    if node in _visited:
        return
    _visited.add(node)
    for neighbour in graph[node]:
        depth_first_walk_graph(graph, neighbour, _visited)
_

私はトピックから外れているかもしれませんが、可変数の引数を渡すだけの場合、Pythonの方法はTuple _*args_または辞書_**kargs_を渡すことです。これらはオプションであり、構文myFunc([1, 2, 3])よりも優れています。

タプルを渡したい場合:

_def myFunc(arg1, *args):
  print args
  w = []
  w += args
  print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]
_

辞書を渡す場合:

_def myFunc(arg1, **kargs):
   print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}
_
1
Mapad

すでに適切で正しい答えが提供されています。私はあなたがやりたいことを書くために別の構文を与えたかっただけです。あなたが例えばデフォルトの空のリストでクラスを作りたいとき、私はもっと美しいと思います

class Node(object):
    def __init__(self, _id, val, parents=None, children=None):
        self.id = _id
        self.val = val
        self.parents = parents if parents is not None else []
        self.children = children if children is not None else []

このスニペットは、if else演算子構文を使用します。コロンなどを含まないきちんとした小さなワンライナーで、通常の英語の文章のように読めるので、特に気に入っています。 :)

あなたの場合、あなたは書くことができます

def myFunc(working_list=None):
    working_list = [] if working_list is None else working_list
    working_list.append("a")
    print working_list
0
drssdinblck

UCSC拡張クラスPython for programmer

これは真実です:def Fn(data = []):

a)呼び出しごとにデータリストが空になるようにすることをお勧めします。

b)呼び出しで引数を提供しない関数へのすべての呼び出しがデータとして空のリストを取得するように、良いアイデアです。

c)データが文字列のリストである限り、合理的な考えです。

d)デフォルトの[]はデータを蓄積し、デフォルトの[]は後続の呼び出しで変更されるため、悪い考えです。

回答:

d)デフォルトの[]はデータを蓄積し、デフォルトの[]は後続の呼び出しで変更されるため、悪い考えです。

0
Peter Chen