Python 描述符和继承
Python descriptors and inheritance
我正在尝试编写一个描述符来调用 cls.method 和 obj.method 上的不同函数。
以下工作但中断了继承
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 可以从 属性 对象调用 class 我做错了什么?
编辑:更新示例
所以我发现了这个问题。问题是 @A.a.method
正在调用 __get__
属性通过在 obj is None
时返回 self
来避免此问题,但是,我显然不能使用该技巧,因为我想在 [= 时分派到 class 方法15=].
解决方案是使用 metaclass
将动态方法对象纳入继承范围 class
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
编辑:为避免 'using up' 元class,将元class 添加到示例
我正在尝试编写一个描述符来调用 cls.method 和 obj.method 上的不同函数。
以下工作但中断了继承
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 可以从 属性 对象调用 class 我做错了什么?
编辑:更新示例
所以我发现了这个问题。问题是 @A.a.method
正在调用 __get__
属性通过在 obj is None
时返回 self
来避免此问题,但是,我显然不能使用该技巧,因为我想在 [= 时分派到 class 方法15=].
解决方案是使用 metaclass
将动态方法对象纳入继承范围 classclass 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
编辑:为避免 'using up' 元class,将元class 添加到示例