我擺弄着繼承,發現了一個似乎對我來說很陌生的行爲 - 即有時我可以重寫父裝飾函數(用於驗證),但有時候我不行,我不明白爲什麼或者有什麼不同。覆蓋裝飾的子類方法
單詞中的快速入門---我有一個人物對象我希望子類更特殊的人物對象。更具體的人會有一個額外的領域,「舞蹈」,並會在以前的領域,「名稱」有不同的驗證規則。
這裏是我的基本情況,其工作原理:
# 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"})
這工作得很好。因此,創建第一個對象bill,使得驗證is_str
成功。如果你嘗試在那裏輸入一個數字,它就會失敗。第二個對象同樣接受非字符串,所以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失敗。該錯誤提示,仍然被應用在__init__
的is_Name
驗證功能:
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',{}))
的字符串名稱「弗雷德」。
但它似乎不能,因爲在前面的例子中,我能夠刪除一個完全矛盾的驗證(is_str
與is_not_str
)。爲什麼這個減去相反但失敗更多?在第一種情況下,它不是同時應用is_str
和is_not_str
,爲什麼它/現在/將is_Name
和is_str
應用於看似相同的語法?
我的問題是:這樣做的第一種方式有什麼不同,導致它從第二種方式獲得成功?我試圖在這裏隔離變量,但不明白爲什麼我可以撤消在方案I中從父類繼承的包裝驗證器,但不能做方案II中看起來類似的東西。看起來唯一有意義的區別是它是一個對象而不是字符串。我知道更好的體系結構方式可以有更多的抽象父類,沒有需要改變的驗證規則---而且這兩種類型的人都可以繼承,但我也明白我應該能夠改變在子類中的方法,所以我想至少明白爲什麼一個成功和失敗等這裏的區別。)
我看,這是有道理的---對不起,這是我做了一個不同的錯誤,但起控制作用的都是一樣的。我已經糾正它,以便我有一個新的名稱__init__方法。然而,它顯然仍然首先調用原始方法,並給我同樣的錯誤。我將如何去做方案II中的初始化/這個變量名/第二種情況下的不同? (而不必重寫兩者共用的'url'部分?) – Mittenchops
對不起,在場景II中調用錯誤的方法對我來說並不明顯。追溯是什麼?您可能需要使用該信息更新您的問題。 –
您可以創建名稱*可選*。如果's'中沒有'name'鍵,則根本不要設置'self.name'。然後在'Crazyperson'中設置名字,從's'中刪除鍵並繼續。 –