如何根据属性访问类型(使用 class 或实例)在函数之间创建 "switch"?
How to make a "switch" between functions, depending on attribute access type (using class or instance)?
免责声明:
这篇文章与其说是一个问题,不如说是一个秘诀,但我发现这个主题很有趣,在网络上几乎没有参考资料。
如果 Whosebug 上有更好的地方可以发布这类文章,请告诉我。
主题:
如何根据属性访问类型(使用 class 或实例)强制 Python 调用不同的函数 - 例如强制 Python 为 MyClass.my_method()
和 MyClass().my_method()
?
调用不同的方法
用例:
比方说,我们有自定义枚举实现(基于 Python36 枚举,但有一些自定义)。作为这个 Enum 的用户,我们想创建一个 CustomEnum,不仅继承 Enum,还继承 str: class MyEnum(str, Enum)
。我们还想添加编码和解码功能。我们的想法是使用 MyEnum.encode
来编码任何对象,包括我们的枚举成员,但保留原始 str.encode
对我们的枚举 class 实例的权力。
简而言之:MyEnum.encode
调用我们的自定义编码函数,并且从这个角度来看具有完美的意义。 MyEnum()
是一个字符串,所以 MyEnum().encode
应该调用继承自 str class.
的编码函数
解法:
写一个描述符,它将作为一个开关。
完整答案在我的第一个 post.
解法:
据我所知,描述符是唯一可以区分的对象,如果它们是为 class 或实例调用,因为 __get__
函数签名:__get__(self, instance, instance_type)
. 属性 允许我们在其上构建一个开关。
class boundmethod(object):
def __init__(self, cls_method=None, instance_method=None, doc=None):
self._cls_method = cls_method
self._instance_method = instance_method
if cls_method:
self._method_name = cls_method.__name__
elif instance_method:
self._method_name = instance_method.__name__
if doc is None and cls_method is not None:
doc = cls_method.__doc__
self.__doc__ = doc
self._method = None
self._object = None
def _find_method(self, instance, instance_type, method_name):
for base in instance_type.mro()[1:]:
method = getattr(base, method_name, None)
if _is_descriptor(method):
method = method.__get__(instance, base)
if method and method is not self:
try:
return method.__func__
except AttributeError:
return method
def __get__(self, instance, instance_type):
if instance is None:
self._method = self._cls_method or self._find_method(instance, instance_type, self._method_name)
self._object = instance_type
else:
self._method = self._instance_method or self._find_method(instance, instance_type, self._method_name)
self._object = instance
return self
@staticmethod
def cls_method(obj=None):
def constructor(cls_method):
if obj is None:
return boundmethod(cls_method, None, cls_method.__doc__)
else:
return type(obj)(cls_method, obj._instance_method, obj.__doc__)
if isinstance(obj, FunctionType):
return boundmethod(obj, None, obj.__doc__)
else:
return constructor
@staticmethod
def instance_method(obj=None):
def constructor(instance_method):
if obj is None:
return boundmethod(None, instance_method, instance_method.__doc__)
else:
return type(obj)(obj._cls_method, instance_method, obj.__doc__)
if isinstance(obj, FunctionType):
return boundmethod(None, obj, obj.__doc__)
else:
return constructor
def __call__(self, *args, **kwargs):
if self._method:
try:
return self._method(self._object, *args, **kwargs)
except TypeError:
return self._method(*args, **kwargs)
return None
示例:
>>> class Walkmen(object):
... @boundmethod.cls_method
... def start(self):
... return 'Walkmen start class bound method'
... @boundmethod.instance_method(start)
... def start(self):
... return 'Walkmen start instance bound method'
>>> print Walkmen.start()
Walkmen start class bound method
>>> print Walkmen().start()
Walkmen start instance bound method
希望对大家有所帮助。
最佳。
实际上我只是问了这个问题( I hadn't seen this question). 使用描述符和元类进行继承。
来自 :
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 ...> defined in A
Called from class <class 'B'> defined in A
Called from obj <B object at ...> defined in B
免责声明:
这篇文章与其说是一个问题,不如说是一个秘诀,但我发现这个主题很有趣,在网络上几乎没有参考资料。
如果 Whosebug 上有更好的地方可以发布这类文章,请告诉我。
主题:
如何根据属性访问类型(使用 class 或实例)强制 Python 调用不同的函数 - 例如强制 Python 为 MyClass.my_method()
和 MyClass().my_method()
?
用例:
比方说,我们有自定义枚举实现(基于 Python36 枚举,但有一些自定义)。作为这个 Enum 的用户,我们想创建一个 CustomEnum,不仅继承 Enum,还继承 str: class MyEnum(str, Enum)
。我们还想添加编码和解码功能。我们的想法是使用 MyEnum.encode
来编码任何对象,包括我们的枚举成员,但保留原始 str.encode
对我们的枚举 class 实例的权力。
简而言之:MyEnum.encode
调用我们的自定义编码函数,并且从这个角度来看具有完美的意义。 MyEnum()
是一个字符串,所以 MyEnum().encode
应该调用继承自 str class.
解法:
写一个描述符,它将作为一个开关。 完整答案在我的第一个 post.
解法:
据我所知,描述符是唯一可以区分的对象,如果它们是为 class 或实例调用,因为 __get__
函数签名:__get__(self, instance, instance_type)
. 属性 允许我们在其上构建一个开关。
class boundmethod(object):
def __init__(self, cls_method=None, instance_method=None, doc=None):
self._cls_method = cls_method
self._instance_method = instance_method
if cls_method:
self._method_name = cls_method.__name__
elif instance_method:
self._method_name = instance_method.__name__
if doc is None and cls_method is not None:
doc = cls_method.__doc__
self.__doc__ = doc
self._method = None
self._object = None
def _find_method(self, instance, instance_type, method_name):
for base in instance_type.mro()[1:]:
method = getattr(base, method_name, None)
if _is_descriptor(method):
method = method.__get__(instance, base)
if method and method is not self:
try:
return method.__func__
except AttributeError:
return method
def __get__(self, instance, instance_type):
if instance is None:
self._method = self._cls_method or self._find_method(instance, instance_type, self._method_name)
self._object = instance_type
else:
self._method = self._instance_method or self._find_method(instance, instance_type, self._method_name)
self._object = instance
return self
@staticmethod
def cls_method(obj=None):
def constructor(cls_method):
if obj is None:
return boundmethod(cls_method, None, cls_method.__doc__)
else:
return type(obj)(cls_method, obj._instance_method, obj.__doc__)
if isinstance(obj, FunctionType):
return boundmethod(obj, None, obj.__doc__)
else:
return constructor
@staticmethod
def instance_method(obj=None):
def constructor(instance_method):
if obj is None:
return boundmethod(None, instance_method, instance_method.__doc__)
else:
return type(obj)(obj._cls_method, instance_method, obj.__doc__)
if isinstance(obj, FunctionType):
return boundmethod(None, obj, obj.__doc__)
else:
return constructor
def __call__(self, *args, **kwargs):
if self._method:
try:
return self._method(self._object, *args, **kwargs)
except TypeError:
return self._method(*args, **kwargs)
return None
示例:
>>> class Walkmen(object):
... @boundmethod.cls_method
... def start(self):
... return 'Walkmen start class bound method'
... @boundmethod.instance_method(start)
... def start(self):
... return 'Walkmen start instance bound method'
>>> print Walkmen.start()
Walkmen start class bound method
>>> print Walkmen().start()
Walkmen start instance bound method
希望对大家有所帮助。
最佳。
实际上我只是问了这个问题(
来自
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 ...> defined in A
Called from class <class 'B'> defined in A
Called from obj <B object at ...> defined in B