是否有类似 getattr/hasattr 但绕过实例属性的功能?
Are there any functions like getattr/hasattr but bypassing instance attributes?
有没有内置函数之类的函数getattr
and hasattr
in the standard library but which bypass instance attributes during attribute lookup, like the implicit lookup of special methods?
我们称这些假设函数为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
用例是内置 class classmethod
:*
的纯 Python 实现
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 instead of hasattr here.
if hasclassattr(self.__func__, '__get__'):
# Note that we use getclassattr instead of getattr here.
return getclassattr(self.__func__, '__get__')(owner, type(owner))
return types.MethodType(self.__func__, owner)
@property
def __isabstractmethod__(self):
return hasattr(self.__func__, '__isabstractmethod__')
* 请注意,此实现不适用于内置函数 getattr
和 hasattr
,因为它们首先在实例属性中查找,因为与内置函数 class classmethod
显示:
>>> 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):
... def __get__(self, instance, owner=None):
... return 'metaclass'
...
>>> class A(metaclass=M):
... def __get__(self, instance, owner=None):
... return 'class'
...
>>> ClassMethod(A).__get__('foo')
'class'
>>> classmethod(A).__get__('foo')
'metaclass'
与其引入新函数 getclassattr
和 hasclassattr
以在属性查找期间绕过实例属性,如 implicit lookup of special methods,另一种方法是引入代理 class(让我们称之为 bypass
)覆盖方法 __getattribute__
。我认为这可能是一种更好的方法,因为方法 __getattribute__
是一个为自定义属性查找而设计的挂钩,它与内置函数 getattr
和 hasattr
一起使用,但也与属性一起使用检索运算符 .
:
class bypass:
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' and getattr(bypass(a), 'x') == 'class'
assert getattr(A, 'x') == 'class' and getattr(bypass(A), 'x') == 'metaclass'
有没有内置函数之类的函数getattr
and hasattr
in the standard library but which bypass instance attributes during attribute lookup, like the implicit lookup of special methods?
我们称这些假设函数为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
用例是内置 class classmethod
:*
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 instead of hasattr here.
if hasclassattr(self.__func__, '__get__'):
# Note that we use getclassattr instead of getattr here.
return getclassattr(self.__func__, '__get__')(owner, type(owner))
return types.MethodType(self.__func__, owner)
@property
def __isabstractmethod__(self):
return hasattr(self.__func__, '__isabstractmethod__')
* 请注意,此实现不适用于内置函数 getattr
和 hasattr
,因为它们首先在实例属性中查找,因为与内置函数 class classmethod
显示:
>>> 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):
... def __get__(self, instance, owner=None):
... return 'metaclass'
...
>>> class A(metaclass=M):
... def __get__(self, instance, owner=None):
... return 'class'
...
>>> ClassMethod(A).__get__('foo')
'class'
>>> classmethod(A).__get__('foo')
'metaclass'
与其引入新函数 getclassattr
和 hasclassattr
以在属性查找期间绕过实例属性,如 implicit lookup of special methods,另一种方法是引入代理 class(让我们称之为 bypass
)覆盖方法 __getattribute__
。我认为这可能是一种更好的方法,因为方法 __getattribute__
是一个为自定义属性查找而设计的挂钩,它与内置函数 getattr
和 hasattr
一起使用,但也与属性一起使用检索运算符 .
:
class bypass:
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' and getattr(bypass(a), 'x') == 'class'
assert getattr(A, 'x') == 'class' and getattr(bypass(A), 'x') == 'metaclass'