我有一个叫做Node的东西。定义和定理都是一种节点,但只允许定义具有plural
属性:
class Definition(Node):
def __init__(self,dic):
self.type = "definition"
super(Definition, self).__init__(dic)
self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)
@property
def plural(self):
return self._plural
@plural.setter
def plural(self, new_plural):
if new_plural is None:
self._plural = None
else:
clean_plural = check_type_and_clean(new_plural, str)
assert dunderscore_count(clean_plural)>=2
self._plural = clean_plural
class Theorem(Node):
def __init__(self, dic):
self.type = "theorem"
super().__init__(dic)
self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)
# theorems CANNOT have plurals:
# if 'plural' in self:
# raise KeyError('Theorems cannot have plurals.')
如您所见,定义有plural.setter
,但定理没有。但是,代码
theorem = Theorem(some input)
theorem.plural = "some plural"
运行得很好,没有错误。但我希望它引发错误。正如您所看到的,我尝试在显示的代码底部手动检查复数,但这只是一个补丁。我想阻止没有明确定义的任何属性的设置。这种事情的最佳做法是什么?
我正在寻找满足" chicken" requirement的答案:
我认为这不能解决我的问题。在你的两个解决方案中,我都可以 附上代码t.chicken =' hi&#39 ;;打印(t.chicken),打印喜 没有错误。我不希望用户能够弥补新的 像鸡一样的属性。
答案 0 :(得分:4)
简短的回答是“是的,你可以。”
后续问题是“为什么?” Python的优势之一是非凡的活力,通过限制这种能力,你实际上使你的课程变得不那么有用(但是请参见底部的编辑)。
但是,有充分的理由限制,如果您选择沿着这条路线前进,则需要修改__setattr__
方法:
def __setattr__(self, name, value):
if name not in ('my', 'attribute', 'names',):
raise AttributeError('attribute %s not allowed' % name)
else:
super().__setattr__(name, value)
不需要使用__getattr__
或__getattribute__
,因为它们不会返回不存在的属性。
以下是您的代码,稍加修改 - 我将__setattr__
方法添加到Node
,并将_allowed_attributes
添加到Definition
和Theorem
。< / p>
class Node:
def __setattr__(self, name, value):
if name not in self._allowed_attributes:
raise AttributeError('attribute %s does not and cannot exist' % name)
super().__setattr__(name, value)
class Definition(Node):
_allowed_attributes = '_plural', 'type'
def __init__(self,dic):
self.type = "definition"
super().__init__(dic)
self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)
@property
def plural(self):
return self._plural
@plural.setter
def plural(self, new_plural):
if new_plural is None:
self._plural = None
else:
clean_plural = check_type_and_clean(new_plural, str)
assert dunderscore_count(clean_plural)>=2
self._plural = clean_plural
class Theorem(Node):
_allowed_attributes = 'type', 'proofs'
def __init__(self, dic):
self.type = "theorem"
super().__init__(dic)
self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)
在使用中它看起来像这样:
>>> theorem = Theorem(...)
>>> theorem.plural = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
AttributeError: attribute plural does not and cannot exist
修改的
考虑到这一点之后,我认为对于你想要的东西是一个很好的折衷方案,并且实际上回答你关于限制只允许更改为setter的问题的一部分,那就是:
_allowed_attributes
元组__setattr__
的{{1}}以始终允许修改/创建至少有一个前导Node
这为您提供了一些保护,可以防止拼写错误和创建您不想要的属性,同时仍然允许程序员根据自己的需要解决或增强类。
好的,新的元类看起来像:
_
class NodeMeta(type):
def __new__(metacls, cls, bases, classdict):
node_cls = super().__new__(metacls, cls, bases, classdict)
allowed_attributes = []
for base in (node_cls, ) + bases:
for name, obj in base.__dict__.items():
if isinstance(obj, property) and hasattr(obj, '__fset__'):
allowed_attributes.append(name)
node_cls._allowed_attributes = tuple(allowed_attributes)
return node_cls
类有两项调整:包括Node
元类,并调整NodeMeta
以仅阻止非下划线的主要属性:
__setattr__
最后,class Node(metaclass=NodeMeta):
def __init__(self, dic):
self._dic = dic
def __setattr__(self, name, value):
if not name[0] == '_' and name not in self._allowed_attributes:
raise AttributeError('attribute %s does not and cannot exist' % name)
super().__setattr__(name, value)
子类Node
和Theorem
将Definition
属性移入类名称空间,因此设置它们没有问题 - 并且作为一个方面请注意,type
是一个糟糕的名称,因为它也是一个内置函数 - 可能是type
而已?
node_type
最后要注意的是:即使这种方法也不能免除实际添加或更改属性的人,因为仍然可以使用class Definition(Node):
type = "definition"
...
class Theorem(Node):
type = "theorem"
...
- 或者(更简单)可以修改object.__setattr__(theorum_instance, 'an_attr', 99)
;然而,如果有人要完成所有这些工作,他们希望知道他们在做什么......如果没有,他们拥有所有的部分。 ;)
答案 1 :(得分:0)
每次访问时都可以检查属性。
@implementation
您可以动态构建允许的方法列表作为装饰器的副作用:
class Theorem(Node):
...
def __getattribute__(self, name):
if name not in ["allowed", "attribute", "names"]:
raise MyException("attribute "+name+" not allowed")
else:
return self.__dict__[name]
def __setattr__(self, name, value):
if name not in ["allowed", "attribute", "names"]:
raise MyException("attribute "+name+" not allowed")
else:
self.__dict__[name] = value
您还需要手动添加非方法属性。
答案 2 :(得分:-1)
是的,您可以创建无法从类外部修改的私有成员。变量名称应以two underscores开头:
class Test(object):
def __init__(self, t):
self.__t = t
def __str__(self):
return str(self.__t)
t = Test(2)
print(t) # prints 2
t.__t = 3
print(t) # prints 2
也就是说,尝试像t.__t = 3
中那样访问这样的变量 而不是 会引发异常。
您可以采用不同的方法来实现所需的行为是使用函数。这种方法需要使用功能表示法“访问属性”,但如果这不打扰你,你可以得到你想要的。以下演示“硬编码”了这些值,但显然您可以让Theorem()
接受一个参数并使用它来动态地为属性设置值。
<强>演示:强>
# -*- coding: utf-8 -*-
def Theorem():
def f(attrib):
def proofs():
return ''
def plural():
return '◊◊◊◊◊◊◊◊'
if attrib == 'proofs':
return proofs()
elif attrib == 'plural':
return plural()
else:
raise ValueError("Attribute [{}] doesn't exist".format(attrib))
return f
t = Theorem()
print(t('proofs'))
print(t('plural'))
print(t('wait_for_error'))
<强>输出强>
◊◊◊◊◊◊◊◊
Traceback (most recent call last):
File "/Users/alfasi/Desktop/1.py", line 40, in <module>
print(t('wait_for_error'))
File "/Users/alfasi/Desktop/1.py", line 32, in f
raise ValueError("Attribute [{}] doesn't exist".format(attrib))
ValueError: Attribute [wait_for_error] doesn't exist