属性() setter 元类中的问题
property() setter issues in metaclass
我正在尝试在我的元class 中使用property()
为access/set 提供一种内部属性。我使用的 property()
与 @property
并列,因为它在 metaclass 中,我需要将 属性 应用于 class 中传递的 [=] 17=]方法。
这是我的代码的基本示例以及相关部分:
def __new__(mcls, name, bases, dct, **kwargs):
def getabstract(obj):
return getattr(obj, '_abstract', False)
def setabstract(obj, value):
if str(value) in ('True', 'False'):
return setattr(obj, '_abstract', value)
else: print(f'invalid abstract assignment {value}')
return None
cls = super().__new__(mcls, name, bases, dct)
for name, value in cls.__dict__.items():
if callable(value):
# only applies to callable (method)
value._abstract = getattr(value, '_abstract', False)
# add an _abstract attribute to the method or return its
# current value if it already has one
for base in bases:
base._abstract = getattr(base, '_abstract', False)
# give each base class an _abstract attribute if not already given
cls.abstract = property(getabstract(cls),
setabstract(cls, getattr(cls, '_abstract', False)))
# make an abstract property for class
for name, value in cls.__dict__.items():
if callable(value):
# make an abstract property for functions
value.abstract = property(getabstract(value),
setabstract(value, getattr(value, '_abstract', False)))
当我 运行 这个时,没有错误发生,但是当访问一个新的 class 由这个 metaclass 创建时,例如Foo.abstract
它 returns:
<property object at 0x.....>
此外,用作 setter 的 setabstract()
函数只设置属性,如果它是 True
或 False
,但是当我做类似 Foo.abstract = 'foo'
,它仍然将值设置为 'foo'
有没有我做错了什么或者我错过了什么?
A 属性 不是实例属性,它是 class 的 descriptor 属性,绑定到访问它的对象。这是一个简化的例子:
class A:
@property
def x(self):
return True
print(A.x) # <property object at 0x0000021AE2944548>
print(A().x) # True
当得到A.x
时,你得到未绑定的属性。获取 A().x
时,属性 绑定到一个实例,A.x.__get__
returns 绑定到一个值。
返回您的代码
这意味着 属性 必须是 class 属性 ,而不是实例属性。或者,在您的情况下,属性 必须是 metaclass 属性。
class Meta(type):
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class Foo(metaclass=Meta):
pass
print(Foo.abstract) # False
Foo.abstract = 'Not at bool' # TypeError: invalid abstract assignment Not at bool
尽管如此,这只能部分解决您的问题,因为您希望您 class 的每个方法都有一个 abstract
属性。为此,您需要 他们的 class 才能拥有 property
.
在我们深入探讨之前,让我回顾一下 Python 中的一个关键概念:我们都是同意的成年人。也许您应该相信您的用户不会将 abstract
设置为 bool
以外的任何其他内容,并让它成为非 属性 属性。特别是,任何用户都可以自行更改 _abstract
属性。
如果这不适合您并且您希望抽象成为 property
,那么一种方法是为包含 property
的方法定义一个包装器 class .
class Abstract:
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class AbstractCallable(Abstract):
def __init__(self, f):
self.callable = f
def __call__(self, *args, **kwargs):
return self.callable(*args, **kwargs)
class Meta(type, Abstract):
def __new__(mcls, name, bases, dct, **kwargs):
for name, value in dct.items():
if callable(value):
dct[name] = AbstractCallable(value)
return super().__new__(mcls, name, bases, dct)
class Foo(metaclass=Meta):
def bar(self):
pass
print(Foo.abstract) # False
print(Foo.bar.abstract) # False
Foo.bar.abstract = 'baz' # TypeError: invalid abstract assignment baz
我正在尝试在我的元class 中使用property()
为access/set 提供一种内部属性。我使用的 property()
与 @property
并列,因为它在 metaclass 中,我需要将 属性 应用于 class 中传递的 [=] 17=]方法。
这是我的代码的基本示例以及相关部分:
def __new__(mcls, name, bases, dct, **kwargs):
def getabstract(obj):
return getattr(obj, '_abstract', False)
def setabstract(obj, value):
if str(value) in ('True', 'False'):
return setattr(obj, '_abstract', value)
else: print(f'invalid abstract assignment {value}')
return None
cls = super().__new__(mcls, name, bases, dct)
for name, value in cls.__dict__.items():
if callable(value):
# only applies to callable (method)
value._abstract = getattr(value, '_abstract', False)
# add an _abstract attribute to the method or return its
# current value if it already has one
for base in bases:
base._abstract = getattr(base, '_abstract', False)
# give each base class an _abstract attribute if not already given
cls.abstract = property(getabstract(cls),
setabstract(cls, getattr(cls, '_abstract', False)))
# make an abstract property for class
for name, value in cls.__dict__.items():
if callable(value):
# make an abstract property for functions
value.abstract = property(getabstract(value),
setabstract(value, getattr(value, '_abstract', False)))
当我 运行 这个时,没有错误发生,但是当访问一个新的 class 由这个 metaclass 创建时,例如Foo.abstract
它 returns:
<property object at 0x.....>
此外,用作 setter 的 setabstract()
函数只设置属性,如果它是 True
或 False
,但是当我做类似 Foo.abstract = 'foo'
,它仍然将值设置为 'foo'
有没有我做错了什么或者我错过了什么?
A 属性 不是实例属性,它是 class 的 descriptor 属性,绑定到访问它的对象。这是一个简化的例子:
class A:
@property
def x(self):
return True
print(A.x) # <property object at 0x0000021AE2944548>
print(A().x) # True
当得到A.x
时,你得到未绑定的属性。获取 A().x
时,属性 绑定到一个实例,A.x.__get__
returns 绑定到一个值。
返回您的代码
这意味着 属性 必须是 class 属性 ,而不是实例属性。或者,在您的情况下,属性 必须是 metaclass 属性。
class Meta(type):
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class Foo(metaclass=Meta):
pass
print(Foo.abstract) # False
Foo.abstract = 'Not at bool' # TypeError: invalid abstract assignment Not at bool
尽管如此,这只能部分解决您的问题,因为您希望您 class 的每个方法都有一个 abstract
属性。为此,您需要 他们的 class 才能拥有 property
.
在我们深入探讨之前,让我回顾一下 Python 中的一个关键概念:我们都是同意的成年人。也许您应该相信您的用户不会将 abstract
设置为 bool
以外的任何其他内容,并让它成为非 属性 属性。特别是,任何用户都可以自行更改 _abstract
属性。
如果这不适合您并且您希望抽象成为 property
,那么一种方法是为包含 property
的方法定义一个包装器 class .
class Abstract:
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class AbstractCallable(Abstract):
def __init__(self, f):
self.callable = f
def __call__(self, *args, **kwargs):
return self.callable(*args, **kwargs)
class Meta(type, Abstract):
def __new__(mcls, name, bases, dct, **kwargs):
for name, value in dct.items():
if callable(value):
dct[name] = AbstractCallable(value)
return super().__new__(mcls, name, bases, dct)
class Foo(metaclass=Meta):
def bar(self):
pass
print(Foo.abstract) # False
print(Foo.bar.abstract) # False
Foo.bar.abstract = 'baz' # TypeError: invalid abstract assignment baz