インスタンスの属性の値を使用して、インスタンスメソッドにデフォルトの引数を渡します。
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"
ここでの問題は何ですか、なぜこれが機能しないのですか?そして、どうすればこの作品を作ることができますか?
インスタンスが存在する前のメソッドが定義されるとデフォルト値が評価されるため、これをデフォルト値として実際に定義することはできません。通常のパターンは、代わりに次のようなことをすることです。
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=None):
if formatting is None:
formatting = self.format
print(formatting)
self.format
は、formatting
が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
は一度だけ呼び出され、関数が呼び出される前に発生しました!
Pythonでは、名前self
はnot特別です。これは単にパラメーター名の規則であるため、___init__
_にself
パラメーターがあります。 (実際、___init__
_もそれほど特別ではありません。特にnotは実際にオブジェクトを作成します...これは長い話です)
C("abc").process()
はC
インスタンスを作成し、process
クラスのC
メソッドを検索し、C
インスタンスでそのメソッドを呼び出します最初のパラメーターとして。そのため、指定した場合、self
パラメーターになります。
ただし、そのパラメーターがあったとしても、self
はまだデフォルト値を設定した時点ではスコープ内にないため、def process(self, formatting = self.formatting)
のようなものを書くことはできません。 Pythonでは、関数のコンパイル時にパラメーターのデフォルト値が計算され、関数に「スタック」します。 (これは、_[]
_のようなデフォルトを使用する場合、そのリストが関数呼び出し間の変更を記憶する理由と同じ理由です。)
どうすればこの作品を作ることができますか?
従来の方法は、None
をデフォルトとして使用し、その値を確認して関数内で置き換えることです。目的の特別な値を作成する方が少し安全であることがわかります(呼び出しコードが同じインスタンスを使用しないように非表示にする限り、object
インスタンスで十分です)。 None
。いずれにしても、この値は_==
_ではなくis
で確認する必要があります。
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')
クラス関数を非静的メソッドとして動作させる場合、「自己」をクラス関数の最初の引数として渡す必要があります。
オブジェクト自体を指します。最初の引数として位置が固定されているため、デフォルト引数として「自己」を渡すことができませんでした。
あなたの場合、「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の組み込み関数であるため」
デフォルトの引数にまたがるif-thenのリストを作成する代わりに、「デフォルト」辞書を使用して、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 'です。
メソッド定義で自分にアクセスすることはできません。私の回避策はこれです-
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