85

インスタンスの属性の値を使用して、デフォルトの引数をインスタンスメソッドに渡したい:

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=self.format):
        print(formatting)

それを試してみると、次のエラーメッセージが表示されます。

NameError: name 'self' is not defined

メソッドを次のように動作させたい:

C("abc").process()       # prints "abc"
C("abc").process("xyz")  # prints "xyz"

ここでの問題は何ですか、なぜこれが機能しないのですか?そして、どうすればこれを機能させることができますか?

4

6 に答える 6

101

これをデフォルト値として実際に定義することはできません。デフォルト値は、インスタンスが存在する前のメソッドが定義されたときに評価されるためです。通常のパターンは、代わりに次のようなことを行うことです。

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=None):
        if formatting is None:
            formatting = self.format
        print(formatting)

self.formatformattingの場合にのみ使用されNoneます。


デフォルト値がどのように機能するかを示すために、次の例を参照してください。

def mk_default():
    print("mk_default has been called!")

def myfun(foo=mk_default()):
    print("myfun has been called.")

print("about to test functions")
myfun("testing")
myfun("testing again")

そしてここでの出力:

mk_default has been called!
about to test functions
myfun has been called.
myfun has been called.

一度だけ呼び出されたことに注意してくださいmk_default。これは、関数が呼び出される前に発生しました。

于 2011-11-15T05:37:40.707 に答える
11

Pythonでは、名前selfは特別ではありません。これはパラメータ名の単なる規則であり、それがにselfパラメータがある理由__init__です。(実際に__init__は、それほど特別なことでもありませ。特に、実際にはオブジェクトを作成しません...それは長い話です)

C("abc").process()インスタンスを作成し、クラス内のメソッドをC検索し、インスタンスを最初のパラメーターとしてそのメソッドを呼び出します。したがって、指定した場合はパラメータになります。processCCself

ただし、そのパラメータがあったとしても、デフォルト値を設定した時点ではまだスコープ内にないdef process(self, formatting = self.formatting)ため、のようなものを書くことはできません。selfPythonでは、パラメーターのデフォルト値は関数のコンパイル時に計算され、関数に「スタック」します。[](これは、のようなデフォルトを使用する場合、そのリストが関数の呼び出し間の変更を記憶するのと同じ理由です。)

どうすればこれを機能させることができますか?

従来の方法はNone、デフォルトとして使用し、その値を確認して関数内で置き換えることです。の代わりに、目的のために特別な値を作成する方が少し安全である場合があります(objectインスタンスは、呼び出し元のコードが同じインスタンスを使用しないように非表示にする限り、必要なすべてです)None。いずれにせよ、この値は。isではなく、で確認する必要があり==ます。

于 2011-11-15T05:45:48.460 に答える
5

デフォルトの引数として使用したいのでself.format、これはメソッドがインスタンス固有である必要があることを意味します(つまり、これをクラスレベルで定義する方法はありません)。__init__代わりに、たとえば、クラス中に特定のメソッドを定義できます。ここで、インスタンス固有の属性にアクセスできます。

1つのアプローチはfunctools.partial、メソッドの更新された(特定の)バージョンを取得するために使用することです。

from functools import partial


class C:
    def __init__(self, format):
        self.format = format
        self.process = partial(self.process, formatting=self.format)

    def process(self, formatting):
        print(formatting)


c = C('default')
c.process()
# c.process('custom')  # Doesn't work!
c.process(formatting='custom')

このアプローチでは、対応する引数をキーワードでのみ渡すことができることに注意してください。位置で指定した場合、で競合が発生するためpartialです。

別のアプローチは、メソッドを定義して設定することです__init__

from types import MethodType


class C:
    def __init__(self, format):
        self.format = format

        def process(self, formatting=self.format):
            print(formatting)

        self.process = MethodType(process, self)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')

これにより、引数を位置で渡すこともできますが、メソッドの解決順序がわかりにくくなります(たとえば、IDE検査に影響を与える可能性がありますが、IDE固有の回避策があると思います)。

getattr別のアプローチは、これらの種類の「インスタンス属性のデフォルト」のカスタムタイプを、対応する引数の入力を実行する特別なデコレータと一緒に作成することです。

import inspect


class Attribute:
    def __init__(self, name):
        self.name = name


def decorator(method):
    signature = inspect.signature(method)

    def wrapper(self, *args, **kwargs):
        bound = signature.bind(*((self,) + args), **kwargs)
        bound.apply_defaults()
        bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
                                if isinstance(v, Attribute)})
        return method(*bound.args, **bound.kwargs)

    return wrapper


class C:
    def __init__(self, format):
        self.format = format

    @decorator
    def process(self, formatting=Attribute('format')):
        print(formatting)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
于 2018-10-17T13:12:32.350 に答える
2

メソッド定義で自分自身にアクセスすることはできません。私の回避策はこれです-

class Test:
  def __init__(self):
    self.default_v = 20

  def test(self, v=None):
    v = v or self.default_v
    print(v)

Test().test()
> 20

Test().test(10)
> 10
于 2019-02-21T20:41:25.633 に答える
1

非静的メソッドとして動作させる場合は、クラス関数の最初の引数として「self」を渡す必要があります。

オブジェクト自体を指します。「self」の位置は最初の引数として固定されているため、デフォルトの引数として「self」を渡すことはできませんでした。

あなたの場合、「formatting =self.format」の代わりに「formatting=None」を使用してから、以下のようにコードから値を割り当てます。

[編集]

class c:
        def __init__(self, cformat):
            self.cformat = cformat

        def process(self, formatting=None):
            print "Formating---",formatting
            if formatting == None:
                formatting = self.cformat
                print formatting
                return formatting
            else:
                print formatting
                return formatting

c("abc").process()          # prints "abc"
c("abc").process("xyz")     # prints "xyz"

注:「format」を変数名として使用しないでください。これは、Pythonの組み込み関数であるためです。

于 2011-11-15T05:52:50.683 に答える
0

デフォルトの引数にまたがるif-thenのリストを作成する代わりに、「defaults」ディクショナリを利用し、eval()を使用してクラスの新しいインスタンスを作成できます。

class foo():
    def __init__(self,arg):
        self.arg = arg

class bar():
    def __init__(self,*args,**kwargs):
        #default values are given in a dictionary
        defaults = {'foo1':'foo()','foo2':'foo()'}
        for key in defaults.keys():

            #if key is passed through kwargs, use that value of that key
            if key in kwargs: setattr(self,key,kwargs[key]) 

            #if no key is not passed through kwargs
            #create a new instance of the default value
            else: setattr(self,key, eval(defaults[key]))

これは、デフォルトの引数として別のクラスをインスタンス化するすべてのクラスの先頭にスローします。コンパイル時にPythonがデフォルトを評価することを回避します...よりクリーンなPythonのアプローチが欲しいのですが、lo'です。

于 2019-07-01T14:26:48.960 に答える