Python描述符和继承 [英] Python descriptors and inheritance

查看:99
本文介绍了Python描述符和继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个描述符,以在cls.method和obj.method上调用不同的函数.

I am trying to write a descriptor to call different functions on cls.method and obj.method.

以下方法有效,但继承失败

The following works but breaks on inheritance

class dynamicmethod:
    def __init__(self, f=None, m=None):
        self.f = f
        self.m = m

    def __get__(self, obj, objtype=None):
        if obj is not None and self.f is not None:
            return types.MethodType(self.f, obj)
        elif objtype is not None and self.m is not None:
            return types.MethodType(self.m, objtype)
        else:
            raise AttributeError('No associated method')

    def method(self, f):
        return type(self)(f, self.m)

    def classmethod(self, m):
        return type(self)(self.f, m)

class A:
    @dynamicmethod
    def a(self):
       print('Called from obj {} defined in A'.format(self))

    @a.classmethod
    def a(cls)
       print('Called from class {} defined in A'.format(cls))

# so far everything works

class B(A):
   @A.a.method
   def a(self):
       print('Called from obj {} defined in B'.format(self))
   #AttributeError: 'function' object has no attribute method
   #Same happens for A.a.classmethod

相比之下,可以从属性对象中调用getter/setter/deleter可以在继承类中做什么?

In comparison getter / setter / deleter can be called from property objects can in inheriting class what am I doing wrong?

更新示例

推荐答案

所以我发现了这个问题.问题是@A.a.method 在调用__get__时,属性通过在obj is None时返回self来避免此问题,但是,我显然不能使用该技巧,因为我想在obj is None时分派给类方法.

So I have discovered the issue. The problem is that @A.a.method is calling __get__ the properties avoid this issue by returning self when obj is None, however, I cannot use that trick obviously because I want to dispatch to the classmethod when obj is None.

解决方案是将动态方法对象带到带有元类的继承类的范围内

The solution is to bring the dynamicmethod objects into scope of the inheriting class with a metaclass

class dynamicmethod:
    '''
        Descriptor to allow dynamic dispatch on calls to class.Method vs obj.Method

        fragile when used with inheritence, to inherit and then overwrite or extend
        a dynamicmethod class must have dynamicmethod_meta as its metaclass
    '''
    def __init__(self, f=None, m=None):
        self.f = f
        self.m = m

    def __get__(self, obj, objtype=None):
        if obj is not None and self.f is not None:
            return types.MethodType(self.f, obj)
        elif objtype is not None and self.m is not None:
            return types.MethodType(self.m, objtype)
        else:
            raise AttributeError('No associated method')

    def method(self, f):
        return type(self)(f, self.m)

    def classmethod(self, m):
        return type(self)(self.f, m)

def make_dynamicmethod_meta(meta):
    class _dynamicmethod_meta(meta):
        def __prepare__(name, bases, **kwargs):
            d = meta.__prepare__(name, bases, **kwargs)
            for base in bases:
                for k,v in base.__dict__.items():
                    if isinstance(v, dynamicmethod):
                        if k in d:
                            raise ValueError('Multiple base classes define the same dynamicmethod')
                        d[k] = v
            return d

    return _dynamicmethod_meta

dynamicmethod_meta=make_dynamicmethod_meta(type)

class A(metaclass=dynamicmethod_meta):
    @dynamicmethod
    def a(self):
       print('Called from obj {} defined in A'.format(self))

    @a.classmethod
    def a(cls)
       print('Called from class {} defined in A'.format(cls))

class B(A):
   @a.method
   def a(self):
       print('Called from obj {} defined in B'.format(self))

A.a()
A().a()
B.a()
B().a()

导致:

Called from class <class 'A'> defined in A
Called from obj <A object at 0x7faef7cde7f0> defined in A
Called from class <class 'B'> defined in A
Called from obj <B object at 0x7faef7cde7f0> defined in B

为避免用完"元类,请将元类添加到示例中

edit: to avoid 'using up' the metaclass, add metaclass to example

这篇关于Python描述符和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆