有没有像标准库中的内置函数getattr
和hasattr
这样的函数,但在属性查找过程中会跳过实例属性,比如特殊方法的隐式查找?
让我们将这些假设函数称为getclassattr
和hasclassattr
。以下是我所期望的实现:
null = object()
def getclassattr(obj, name, default=null, /):
if not isinstance(name, str):
raise TypeError('getclassattr(): attribute name must be string')
try:
classmro = vars(type)['__mro__'].__get__(type(obj))
for cls in classmro:
classdict = vars(type)['__dict__'].__get__(cls)
if name in classdict:
attr = classdict[name]
attrclassmro = vars(type)['__mro__'].__get__(type(attr))
for attrclass in attrclassmro:
attrclassdict = vars(type)['__dict__'].__get__(attrclass)
if '__get__' in attrclassdict:
return attrclassdict['__get__'](attr, obj, type(obj))
return attr
classname = vars(type)['__name__'].__get__(type(obj))
raise AttributeError(f'{classname!r} object has no attribute {name!r}')
except AttributeError as exc:
try:
classmro = vars(type)['__mro__'].__get__(type(obj))
for cls in classmro:
classdict = vars(type)['__dict__'].__get__(cls)
if '__getattr__' in classdict:
return classdict['__getattr__'](obj, name)
except AttributeError as exc_2:
exc = exc_2
except BaseException as exc_2:
raise exc_2 from None
if default is not null:
return default
raise exc from None
def hasclassattr(obj, name, /):
try:
getclassattr(obj, name)
except AttributeError:
return False
return True
一个用例是内置类classmethod
的纯Python实现,其中假设函数getclassattr
和hasclassattr
在类type(self.__func__)
:*上查找属性'__get__'
import types
class ClassMethod:
def __init__(self, function):
self.__func__ = function
def __get__(self, instance, owner=None):
if instance is None and owner is None:
raise TypeError('__get__(None, None) is invalid')
if owner is None:
owner = type(instance)
# Note that we use hasclassattr here, not hasattr.
if hasclassattr(self.__func__, '__get__'):
# Note that we use getclassattr here, not getattr.
return getclassattr(self.__func__, '__get__')(owner, type(owner))
return types.MethodType(self.__func__, owner)
@property
def __isabstractmethod__(self):
return hasattr(self.__func__, '__isabstractmethod__')
class M(type):
pass
class C(metaclass=M):
def __get__(self, instance, owner=None):
pass
assert ClassMethod(C).__get__(123) == classmethod(C).__get__(123)
*注意,在self.__func__
上调用假设函数getclassattr
和hasclassattr
而不是在其上调用内置函数getattr
和hasattr
是不起作用的,因为它们在实例self.__func__
和类type(self.__func__)
:上都查找属性'__get__'
import types
class ClassMethod:
def __init__(self, function):
self.__func__ = function
def __get__(self, instance, owner=None):
if instance is None and owner is None:
raise TypeError('__get__(None, None) is invalid')
if owner is None:
owner = type(instance)
if hasattr(self.__func__, '__get__'):
return getattr(self.__func__, '__get__')(owner, type(owner))
return types.MethodType(self.__func__, owner)
@property
def __isabstractmethod__(self):
return hasattr(self.__func__, '__isabstractmethod__')
class M(type):
pass
class C(metaclass=M):
def __get__(self, instance, owner=None):
pass
assert ClassMethod(C).__get__(123) != classmethod(C).__get__(123)
在type(self.__func__)
上调用内置函数getattr
和hasattr
也不起作用,因为它们在实例type(self.__func__)
和类type(type(self.__func__))
:上都查找属性'__get__'
import types
class ClassMethod:
def __init__(self, function):
self.__func__ = function
def __get__(self, instance, owner=None):
if instance is None and owner is None:
raise TypeError('__get__(None, None) is invalid')
if owner is None:
owner = type(instance)
if hasattr(type(self.__func__), '__get__'):
return getattr(type(self.__func__), '__get__')(owner, type(owner))
return types.MethodType(self.__func__, owner)
@property
def __isabstractmethod__(self):
return hasattr(self.__func__, '__isabstractmethod__')
class MM(type):
def __get__(self, instance, owner=None):
pass
class M(type, metaclass=MM):
pass
class C(metaclass=M):
pass
assert ClassMethod(C).__get__(123) != classmethod(C).__get__(123)
在属性查找过程中引入新函数getclassattr
和hasclassattr
来跳过实例属性,而不是像特殊方法的隐式查找一样,另一种方法是引入一个覆盖方法__getattribute__
的代理类(我们称之为skip
(。我认为这是一种更好的方法,因为方法__getattribute__
是一个为自定义属性查找而设计的钩子,它与内置函数getattr
和hasattr
一起工作,也与属性检索运算符.
:一起工作
class skip:
def __init__(self, subject):
self.subject = subject
def __getattribute__(self, name):
obj = super().__getattribute__('subject')
classmro = vars(type)['__mro__'].__get__(type(obj))
for cls in classmro:
classdict = vars(type)['__dict__'].__get__(cls)
if name in classdict:
attr = classdict[name]
attrclassmro = vars(type)['__mro__'].__get__(type(attr))
for attrclass in attrclassmro:
attrclassdict = vars(type)['__dict__'].__get__(attrclass)
if '__get__' in attrclassdict:
return attrclassdict['__get__'](attr, obj, type(obj))
return attr
classname = vars(type)['__name__'].__get__(type(obj))
raise AttributeError(
f'{classname!r} object has no attribute {name!r}')
class M(type):
x = 'metaclass'
class A(metaclass=M):
x = 'class'
a = A()
a.x = 'object'
assert getattr(a, 'x') == 'object'
assert getattr(skip(a), 'x') == 'class'
assert getattr(A, 'x') == 'class'
assert getattr(skip(A), 'x') == 'metaclass'