我们如何使如下代码模拟__getattribute__
继承自的方法object
?我真的很想修改的行为,__getattribute__
但我想从获得自然行为开始。
class Descriptor:
def __get__(*args):
pass
def __set__(*args)
pass
class K:
__getattribute__ = Descriptor()
我们如何使如下代码模拟__getattribute__
继承自的方法object
?我真的很想修改的行为,__getattribute__
但我想从获得自然行为开始。
class Descriptor:
def __get__(*args):
pass
def __set__(*args)
pass
class K:
__getattribute__ = Descriptor()
是的,我们可以做__getattribute__
一个描述符!首先,如果存在一个名为 的整数成员变量x
,那么__get__
为其编写的描述符方法x
应该返回一个整数。同样,因为__getattribute__
is 是一个函数,所以__get__
for__getattribute__
将返回一个函数。最初,让我们__get__
返回一个名为 的愚蠢函数foo
,这样我们就可以运行程序看看发生了什么:
class Descriptor:
def __get__(*args):
print("ENTERING __get__")
print("Type(arg) for args passed into __get__: ", end="")
print(", ".join(map(lambda x: type(x).__name__, args)))
def foo(*args):
print("ENTERING `foo`")
print("args passed into foo: ", end="")
print(", ".join(repr(arg) for arg in args))
print("LEAVING `foo`")
return "I AM THE RETURN VALUE OF FOO"
return foo
class K:
__getattribute__ = Descriptor()
现在,我们尝试以下方法:
instance = K()
x = instance.x
以下是在控制台 ( stdout
) 上看到的语句:
ENTERING __get__
Type(arg) for args passed into __get__: Descriptor, K, type
ENTERING `foo`
args passed into foo: 'x'
LEAVING `foo`
请注意,foo
不通过通常的self
参数接收实例对象。相反,只foo
接收属性名称'x'
通常,以下两段代码产生相同的最终结果:
x = instance.x
x = K.__getattribute__(instance, 'x')
让我们尝试运行K.__getattribute__(instance, 'x')
:
ENTERING __get__
Type(arg) for args passed into __get__: Descriptor, NoneType, type
ENTERING `foo`
args passed into foo: <__main__.K object at 0x01AFE0B8>, 'x'
LEAVING `foo`
input to__get__
和 input tofoo
实际上和以前不同了。
+---------------------+-----------------------------+----------------------------+
| instance.x | __get__( | foo(<<string `x`>>) |
| | <<descriptor instance>>, | |
| | <<instance of K class>>, | |
| | <<K class itself>> | |
| | ) | |
+---------------------+-----------------------------+----------------------------+
| K.__getattribute__( | __get__( | foo( |
| instance, 'x' | <<descriptor instance>>, | <<instance of K class>>,|
| ) | <<None>>, | <<string `x`>> |
| | <<K class itself>>, | |
| | | |
+---------------------+-----------------------------+----------------------------+
因此,我们希望__get__
如下所示:
def __get__(descriptor, Kinstance, Kclass):
if Kinstance:
"""
return a function, `f`, which accepts as its only input
the string name of an attribute.
leftChild = f('leftChild')
`f` is supposed to return that attribute
"""
else: # Kinstance == None
"""
return a function, `f`, which accepts as input two items:
1) instance of some class
2)the string name of an attribute.
inst = Klass()
leftChild = f(inst, 'leftChild')
`f` is supposed to return that attribute
"""
如果您想要 的默认行为__getattribute__
,以下几乎可以工作:
class Descriptor:
def __get__(descriptor, self, cls):
if self:
lamby = lambda attrname:\
object.__getattribute__(self, attrname)
return lamby
else: # self == None
return object.__getattribute__
class K:
__getattribute__ = Descriptor()
在self
命名的 lambda 函数中作为全局变量lamby
是非常危险的。当 lambda 函数最终被调用时,self
将与self
定义 lambda 函数时存在的不同。考虑以下示例:
color = "white"
get_fleece_color = lambda shoop:\
shoop + ", whose fleece was as " + color + " as snow."
color
是 lambda 函数内部的成员变量get_fleece_color
。get_fleece_color
定义的时间color
是"white"
,但这可能会改变:
print(get_fleece_color("Igor"))
# [... many lines of code later...]
color = "pink polka-dotted"
print(get_fleece_color("Igor's cousin, 3 times removed"))
输出是:
Igor, whose fleece was white as snow.
Igor's cousin, 3 times removed Igor, whose fleece was as pink polka-dotted as snow.
def
使用关键字而不是make 的函数lambda
同样危险。
lamby = lambda attrname:\
object.__getattribute__(self, attrname)
def lamby(attrname):
object.__getattribute__(self, attrname)
我们希望使用在定义self
的同时存在的值lamby
,而不是使用被调用self
时存在的值lamby
。有几种方法可以做到这一点,其中 3 种如下所示:
lamby = lambda attrname, *, self=self:\
object.__getattribute__(self, attrname)
class Descriptor:
def __get__(descriptor, self, cls):
lamby = object.__getattribute__
if self: # self != None
Method = type(descriptor.__get__)
lamby = Method(object.__getattribute__, self)
return lamby
class SamMethod:
def __init__(self, func, arg):
self.func = func
self.arg = arg
def __call__(self, *args, **kwargs):
return self.func(self.arg, *args, **kwargs)
class Descriptor:
def __get__(descriptor, self, cls):
lamby = object.__getattribute__
if self: # self != None
lamby = SamMethod(object.__getattribute__, self)
return lamby
class K:
__getattribute__ = Descriptor()
def foo(self):
pass
解决方案2存在一个问题,即method
类构造函数的签名可能会在未来版本的 python 中发生变化。解决方案3是我最喜欢的,但请随意选择您自己的。
继承工作得很好:
class SamMethod:
def __init__(self, func, arg):
self.func = func
self.arg = arg
def __call__(self, *args, **kwargs):
return self.func(self.arg, *args, **kwargs)
#############################################################################
class Descriptor:
def __get__(descriptor, self, cls):
print(
"MESSAGE"
)
lamby = object.__getattribute__
if self: # self != None
lamby = SamMethod(object.__getattribute__, self)
return lamby
###############################################################################
class ParentOfKlaus:
__getattribute__ = Descriptor()
class Klaus(ParentOfKlaus):
def foo(self):
print("Klaus.foo")
class ChildOfKlaus(Klaus):
def __init__(self):
self.x = 99
def foo(self):
print("ChildOfKlaus.foo")
instance = ChildOfKlaus()
y = instance.x
print(y) # 99
instance.foo()
控制台输出为:
MESSAGE
99
MESSAGE
ChildOfKlaus.foo
__getattribute__
描述符而不是覆盖__getattribute__
?一个优点是我们可以访问传递给描述符__get__
方法的类参数。注意__get__
被称为 as__get__(descriptor, Kinstance, Kclass)
我们通常不能直接访问Kclass
,但是如果__getattribute__
写成描述符,那么它可以访问Kclass