gpt4 book ai didi

python-3.x - Monkeypatching 一个 Python 类

转载 作者:行者123 更新时间:2023-12-05 03:28:40 27 4
gpt4 key购买 nike

我想了解 Python 类和对象的工作原理。在 Perl 中它非常简单,在 package 中定义的每个 sub 都可以作为静态、类或对象方法调用(CLASS::funcCLASS->func$obj->func)。乍一看,Python 类看起来像带有 bless 哈希(Python 类中的 __dict__ 属性)的 Perl 类。但是在 Python 中我有点困惑。因此,为了更好地理解,我尝试猴子修补一个空类,添加 3 个行为与静态、类和对象方法完全一样的属性,但我无法得到它。

首先我创建了普通类来获取基本结果:

def say(msg, x):
print('*', msg, 'x =', x, 'type(x) =', type(x))


class A():
@staticmethod
def stt_m(x):
say('stt_m', x)

@classmethod
def cls_m(x):
say('cls_m', x)

def obj_m(x):
say('obj_m', x)

然后我创建了一个函数(称为 test),它尝试使用一个参数调用所有方法,如果失败(因为第一个参数可以是类或对象本身),则尝试再次调用none 在输出行前面打印一个“X”,然后打印检测到的类型:

def test(obj):
# Detect if obj is a class or an instantiated object
what = 'Class' if type(obj) == type else 'Object'
print()
# Try to call static, class and object method getting attributes
for a in ('stt_m', 'cls_m', 'obj_m'):
meth = getattr(obj, a)
try:
meth(111)
except:
print('X', end='')
meth()
print(' ', what, a, meth)

使用默认的 A 类及其对象调用 test:

test(A)
test(A())

结果是:

* stt_m x = 111 type(x) = <class 'int'>
Class stt_m <function A.stt_m at 0x7fb37e63c8c8>
X* cls_m x = <class '__main__.A'> type(x) = <class 'type'>
Class cls_m <bound method A.cls_m of <class '__main__.A'>>
* obj_m x = 111 type(x) = <class 'int'>
Class obj_m <function A.obj_m at 0x7fb37e63c9d8>

* stt_m x = 111 type(x) = <class 'int'>
Object stt_m <function A.stt_m at 0x7fb37e63c8c8>
X* cls_m x = <class '__main__.A'> type(x) = <class 'type'>
Object cls_m <bound method A.cls_m of <class '__main__.A'>>
X* obj_m x = <__main__.A object at 0x7fb37e871748> type(x) = <class '__main__.A'>
Object obj_m <bound method A.obj_m of <__main__.A object at 0x7fb37e871748>>

因此,使用类或对象前缀调用 staticmethod,它们的行为与普通(命名空间)函数一样(接受 1 个参数)。无论使用哪种方式调用 classmethod,传递的第一个参数都是类对象。从类调用 objectmethod 的行为与普通函数一样,如果从对象调用,则第一个参数是对象本身。这后来看起来有点奇怪,但我可以接受。

现在,让我们尝试猴子修补一个空类:

class B():
pass

B.stt_m = lambda x: say('stt_m', x)
B.cls_m = types.MethodType(lambda x: say('cls_m', x), B)
B.obj_m = types.MethodType(lambda x: say('obj_m', x), B())

test(B)
test(B())

结果是:

* stt_m x = 111 type(x) = <class 'int'>
Class stt_m <function <lambda> at 0x7fbf05ec7840>
X* cls_m x = <class '__main__.B'> type(x) = <class 'type'>
Class cls_m <bound method <lambda> of <class '__main__.B'>>
X* obj_m x = <__main__.B object at 0x7fbf0d7dd978> type(x) = <class '__main__.B'>
Class obj_m <bound method <lambda> of <__main__.B object at 0x7fbf0d7dd978>>

X* stt_m x = <__main__.B object at 0x7fbf06375e80> type(x) = <class '__main__.B'>
Object stt_m <bound method <lambda> of <__main__.B object at 0x7fbf06375e80>>
X* cls_m x = <class '__main__.B'> type(x) = <class 'type'>
Object cls_m <bound method <lambda> of <class '__main__.B'>>
X* obj_m x = <__main__.B object at 0x7fbf0d7dd978> type(x) = <class '__main__.B'>
Object obj_m <bound method <lambda> of <__main__.B object at 0x7fbf0d7dd978>>

根据这种模式,stt_m 的行为类似于普通类的对象方法,cls_mobj_m 的行为类似于普通类的类方法普通类。

我可以用这种方式对静态方法进行猴子修补吗?

最佳答案

您可以将方法猴子修补到一个类上,但它是这样完成的:

B.stt_m = staticmethod(lambda x: say('stt_m', x))
B.cls_m = classmethod(lambda x: say('cls_m', x))
B.obj_m = lambda x: say('obj_m', x)

您的 B.cls_m 版本没问题,但是您的 B.stt_m 创建了一个普通方法,而您的 B.obj_m 附加了一个新创建的 B() 的实例方法,但是 B() 被丢弃了,你测试一个新的 B() 没有额外的方法。

在 Python 中通常不需要使用 types.MethodType:

types.MethodType(function, object_)

相当于

function.__get__(object_)

虽然也很少见,但要好一些。

此外(无关紧要但更简洁),在新版本的 Python 中,您的

print('*', msg, 'x =', x, 'type(x) =', type(x))

可以写成

print(f"* {msg} {x = } {type(x) = }")

关于python-3.x - Monkeypatching 一个 Python 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71197957/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com