我正在摆弄继承,发现了一种对我来说似乎很奇怪的行为——即,有时我可以覆盖父装饰器函数(用于验证),但有时我不能,而且我不明白为什么或什么不同的是。
用文字快速演练 --- 我有一个人对象,我想将其子类化为更具体的人对象。更具体的将有一个额外的字段“舞蹈”,并将在前一个字段“名称”上有不同的验证规则。
这是我的基本案例:
# Define the validation wrapper
def ensure(name, validate, doc=None):
def decorator(Class):
privateName = "__" + name
def getter(self):
return getattr(self, privateName)
def setter(self, value):
validate(name, value)
setattr(self, privateName, value)
setattr(Class, name, property(getter, setter, doc=doc))
return Class
return decorator
# Define the not string validation
def is_not_str(name, value):
if isinstance(value, str):
raise ValueError("{} cannot be a string.".format(name))
# Chosen to be exact opposite of above---demonstrating it's possible to reverse.
def is_str(name, value):
if not isinstance(value, str):
raise ValueError("{} must be a string.".format(name))
@ensure("name", is_str)
@ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = s.get('name',{})
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
@ensure("name", is_not_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s) # idiom to inherit init
self.dance = s.get('dance') # add new param.
bill = Person({"name":"bill",
"url":"http://www.example.com"})
fred = Crazyperson({"name":1,
"url":"http://www.example.com",
"dance":"Flamenco"})
这工作正常。is_str
因此,第一个对象 bill 是以验证成功的方式创建的。如果您尝试将数字放在那里,它会失败。第二个对象同样接受非字符串,所以 fred 被成功创建。
现在,这是它破裂的情况,我想了解...
def is_Name(name, value):
if not isinstance(value, dict) and not isinstance(value,Name):
raise ValueError("{} must be a valid Name object".format(name))
# new object that will be a non-string type of name.
@ensure("firstname", is_str)
@ensure("lastname", is_str)
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
def __str__(self):
return "Name({{'firstname':'{}','lastname':'{}' }})".format(self.firstname, self.lastname)
def __repr__(self):
return str(self)
@ensure("name", is_Name) # require it as the default for the base class
@ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
@ensure("name", is_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.name = s.get('name','') # THIS IS THE KEY
self.dance = s.get('dance')
bill = Person({"name":{"firstname":"Bill", "lastname":"billbertson"},
"url":"http://www.example.com"})
fred = Crazyperson({"name":"Fred",
"url":"http://www.example.com",
"dance":"Flamenco"})
在这种情况下,Crazyperson 失败了。该错误表明仍在应用is_Name
中的验证功能:__init__
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "<stdin>", line 4, in __init__
File "<stdin>", line 5, in __init__
File "<stdin>", line 5, in __init__
AttributeError: 'str' object has no attribute 'get'
看起来它已经调用了Name
初始化器:Name(s.get('name',{}))
在字符串名称“Fred”上。
但似乎不可能,因为在前面的示例中,我能够删除一个完全矛盾的验证(is_str
vs is_not_str
)。为什么这不太相反但失败更多?在第一种情况下,它没有同时应用is_str
and is_not_str
,为什么 /now/ 应用两者is_Name
和is_str
看似相同的语法?
我的问题是:第一种方式与第二种方式相比有什么不同?我试图在这里隔离变量,但不明白为什么我可以撤消从场景 I 中的父类继承的包装验证器,但不能做在场景 II 中看起来相似的事情。似乎唯一有意义的区别是它是一个对象而不是字符串。
(我知道更好的架构方法是拥有第三个更抽象的父类,没有需要更改的验证规则——并且两种人都将从中继承。但我也明白我应该能够更改子类中的方法,所以我想至少了解一个成功与另一个失败之间的区别。)