为什么调用 gettatr 方法需要显式传递 self?
Why does calling a gettatr method require to explicitly pass self?
考虑以下代码(有效)
class AAA:
def run(self):
getattr(AAA, 'meth')(what='hello')
@staticmethod
def meth(what):
print(what)
AAA().run()
我需要在 meth()
中使用实例化对象的属性,因此 meth()
不能再是静态的。我试着做
class AAA:
def __init__(self):
self.who = 'John'
def run(self):
getattr(AAA, 'meth')(what='hello')
def meth(self, what):
print(f"{what} {self.who}")
AAA().run()
但这会与
一起崩溃
Traceback (most recent call last):
File "C:/scratch_15.py", line 12, in <module>
AAA().run()
File "C:/scratch_15.py", line 7, in run
getattr(AAA, 'meth')(what='hello')
TypeError: meth() missing 1 required positional argument: 'self'
然后我将我的代码更正为
class AAA:
def __init__(self):
self.who = 'John'
def run(self):
getattr(AAA, 'meth')(self, what='hello')
def meth(self, what):
print(f"{what} {self.who}")
AAA().run()
# outputs "hello John"
为什么在上述情况下调用方法时必须显式传递self
?
getattr(AAA, 'meth')
完全等同于 AAA.meth
;您没有在属性查找中涉及绑定到 self
的实例,因此您得到的是原始函数,而不是 method
对象。
相反,传递 self
作为第一个参数:
def run(self, methodname):
getattr(self, methodname)(what='hello') # self.meth(what='hello')
甚至忽略 getattr
,类似
a = AAA()
a.run()
等同于
a = AAA()
AAA.run(a)
这与描述符协议有关;由于 function
对象实现了 __get__
方法,因此 AAA.run
和 a.run
实际上都是该方法的 return 值。
在 AAA.run
的情况下,None
作为第一个参数传递,return 值是函数本身。
# AAA.run
>>> AAA.run.__get__(None, AAA)
<function AAA.run at 0x100833048>
在a.run
的情况下,实例a
作为第一个参数传递,结果是一个`方法对象。
# a.run
>>> AAA.run.__get__(a, AAA)
<bound method AAA.run of <__main__.AAA object at 0x1008916a0>>
当method
对象被调用时,它以a
为第一个参数调用原始函数,其余参数为它自己的参数。
这是对class vs instance的误解。 AAA
是 class,而 AAA()
是 class AAA
.[=30 的 实例 =]
请注意 class 中方法的第一个参数总是 self
- 这是因为这些是 instance 方法在您的 实例 上运行。至于 classmethods
它们通常由 cls
定义为第一个参数以避免混淆。 (归根结底,这些参数的命名是任意的,但重要的是位置)。
因此,每当您检索 AAA.meth
(相当于 getattr(AAA, 'meth')
)而没有 实例时,仍会检索属性,但实例对象, self
,因为没有给出所以没有通过。因此,解释器正确地要求知道,这里要使用的实例是什么?
为简化起见,请观察以下内容:
>>> s = 'hello'
>>> s.upper
<built-in method upper of str object at 0x085AA6C0>
>>> str.upper
<method 'upper' of 'str' objects>
>>> str(s).upper
<built-in method upper of str object at 0x085AA6C0>
注意 str
方法 upper
等同于 s.upper
一旦你调用 str(s).upper
,因为 class 现在知道哪个 class它正在与。巧合的是:
>>> str.upper(s)
'HELLO'
>>> str(s).upper()
'HELLO'
>>> str.upper()
Traceback (most recent call last):
File "<pyshell#167>", line 1, in <module>
str.upper()
TypeError: descriptor 'upper' of 'str' object needs an argument
请注意,只要在 class 或方法本身中提供了实例的上下文,它仍然会执行相同的操作。但是当在没有实例的情况下调用时,它期望第一个参数 (self
) 得到满足。使用实例时,实例本身将自动满足方法调用中的第一个位置参数 (self
)。
总而言之——注意你的对象、函数和方法是如何定义的。要实现的第一个参数是 self
,这是您应该传入的内容,而不是 AAA
class 对象。如果预期是实例方法,则直接在实例上工作:
def run(self, methodname, *args, **kwargs):
getattr(self, methodname)(*args, **kwargs)
考虑以下代码(有效)
class AAA:
def run(self):
getattr(AAA, 'meth')(what='hello')
@staticmethod
def meth(what):
print(what)
AAA().run()
我需要在 meth()
中使用实例化对象的属性,因此 meth()
不能再是静态的。我试着做
class AAA:
def __init__(self):
self.who = 'John'
def run(self):
getattr(AAA, 'meth')(what='hello')
def meth(self, what):
print(f"{what} {self.who}")
AAA().run()
但这会与
一起崩溃Traceback (most recent call last):
File "C:/scratch_15.py", line 12, in <module>
AAA().run()
File "C:/scratch_15.py", line 7, in run
getattr(AAA, 'meth')(what='hello')
TypeError: meth() missing 1 required positional argument: 'self'
然后我将我的代码更正为
class AAA:
def __init__(self):
self.who = 'John'
def run(self):
getattr(AAA, 'meth')(self, what='hello')
def meth(self, what):
print(f"{what} {self.who}")
AAA().run()
# outputs "hello John"
为什么在上述情况下调用方法时必须显式传递self
?
getattr(AAA, 'meth')
完全等同于 AAA.meth
;您没有在属性查找中涉及绑定到 self
的实例,因此您得到的是原始函数,而不是 method
对象。
相反,传递 self
作为第一个参数:
def run(self, methodname):
getattr(self, methodname)(what='hello') # self.meth(what='hello')
甚至忽略 getattr
,类似
a = AAA()
a.run()
等同于
a = AAA()
AAA.run(a)
这与描述符协议有关;由于 function
对象实现了 __get__
方法,因此 AAA.run
和 a.run
实际上都是该方法的 return 值。
在 AAA.run
的情况下,None
作为第一个参数传递,return 值是函数本身。
# AAA.run
>>> AAA.run.__get__(None, AAA)
<function AAA.run at 0x100833048>
在a.run
的情况下,实例a
作为第一个参数传递,结果是一个`方法对象。
# a.run
>>> AAA.run.__get__(a, AAA)
<bound method AAA.run of <__main__.AAA object at 0x1008916a0>>
当method
对象被调用时,它以a
为第一个参数调用原始函数,其余参数为它自己的参数。
这是对class vs instance的误解。 AAA
是 class,而 AAA()
是 class AAA
.[=30 的 实例 =]
请注意 class 中方法的第一个参数总是 self
- 这是因为这些是 instance 方法在您的 实例 上运行。至于 classmethods
它们通常由 cls
定义为第一个参数以避免混淆。 (归根结底,这些参数的命名是任意的,但重要的是位置)。
因此,每当您检索 AAA.meth
(相当于 getattr(AAA, 'meth')
)而没有 实例时,仍会检索属性,但实例对象, self
,因为没有给出所以没有通过。因此,解释器正确地要求知道,这里要使用的实例是什么?
为简化起见,请观察以下内容:
>>> s = 'hello'
>>> s.upper
<built-in method upper of str object at 0x085AA6C0>
>>> str.upper
<method 'upper' of 'str' objects>
>>> str(s).upper
<built-in method upper of str object at 0x085AA6C0>
注意 str
方法 upper
等同于 s.upper
一旦你调用 str(s).upper
,因为 class 现在知道哪个 class它正在与。巧合的是:
>>> str.upper(s)
'HELLO'
>>> str(s).upper()
'HELLO'
>>> str.upper()
Traceback (most recent call last):
File "<pyshell#167>", line 1, in <module>
str.upper()
TypeError: descriptor 'upper' of 'str' object needs an argument
请注意,只要在 class 或方法本身中提供了实例的上下文,它仍然会执行相同的操作。但是当在没有实例的情况下调用时,它期望第一个参数 (self
) 得到满足。使用实例时,实例本身将自动满足方法调用中的第一个位置参数 (self
)。
总而言之——注意你的对象、函数和方法是如何定义的。要实现的第一个参数是 self
,这是您应该传入的内容,而不是 AAA
class 对象。如果预期是实例方法,则直接在实例上工作:
def run(self, methodname, *args, **kwargs):
getattr(self, methodname)(*args, **kwargs)