首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Monkeypatching类

Monkeypatching类
EN

Stack Overflow用户
提问于 2022-02-20 19:24:53
回答 1查看 241关注 0票数 3

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

首先,我创建了一个普通类以获得基本结果:

代码语言:javascript
复制
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),它尝试用一个参数调用所有方法,如果失败(因为第一个参数可以是类或对象本身),尝试再次调用,在输出行前面不打印'X‘,然后打印检测到的类型:

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
test(A)
test(A())

结果是:

代码语言:javascript
复制
* 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作为正常函数,如果从对象调用,则第一个参数是对象本身。这以后看上去有点奇怪,但我可以接受。

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

代码语言:javascript
复制
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())

结果是:

代码语言:javascript
复制
* 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的行为类似于普通类的类方法。

我能用这种方式给静态方法打个补丁吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-20 20:33:47

您可以在类上安装猴子补丁方法,但是它是这样完成的:

代码语言:javascript
复制
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

代码语言:javascript
复制
types.MethodType(function, object_)

等于

代码语言:javascript
复制
function.__get__(object_)

这是比较好一点,虽然也非常罕见。

此外,在Python的新版本中,

代码语言:javascript
复制
print('*', msg, 'x =', x, 'type(x) =', type(x))

可以直接写成

代码语言:javascript
复制
print(f"* {msg} {x = } {type(x) = }")
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71197957

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档