i'm fiddling around inheritance , found behavior seems strange me---namely, times can override parent decorator function (used validation), cannot, , cannot understand why or difference is.
a quick walkthrough in words---i have person object i'd subclass more particular person object. more particular 1 have additional field, "dance," , have different validation rules on previous field, "name."
here's base case works:
# define 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 not string validation def is_not_str(name, value): if isinstance(value, str): raise valueerror("{} cannot string.".format(name)) # chosen exact opposite of above---demonstrating it's possible reverse. def is_str(name, value): if not isinstance(value, str): raise valueerror("{} must 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 number rather name() object. class crazyperson(person): def __init__(self,s): super(crazyperson,self).__init__(s) # idiom 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"})
this works fine. so, first object, bill, created in such way validation is_str
succeeds. if try put number there, fails. second object, likewise, accepts non-strings, fred created successfully.
now, here's case breaks, i'd understand...
def is_name(name, value): if not isinstance(value, dict) , not isinstance(value,name): raise valueerror("{} must valid name object".format(name)) # new object 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 default 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 number rather name() object. class crazyperson(person): def __init__(self,s): super(crazyperson,self).__init__(s) self.name = s.get('name','') # 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"})
in instance, crazyperson fails. error suggests is_name
validation function in __init__
still being applied:
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'
it looks has called name
initializer: name(s.get('name',{}))
on string name "fred".
but seems can't be, because in previous example, able remove contradictory validation (is_str
versus is_not_str
). why less opposite failing more? in first case wasn't applying both is_str
, is_not_str
, why /now/ applying both is_name
, is_str
seemingly identical syntax?
my question is: what's different first way of doing causes succeed second way? i've tried isolate variables here, don't understand why can undo wrapped validator inherited parent class in scenario cannot seems similar in scenario ii. seems meaningful difference it's object instead of string.
(i understand better architectural way have third more abstract parent class, no validation rules need changing---and both kinds of person inherit that. understand supposed able change methods in subclasses, i'd @ least understand difference between why 1 succeeding , other failing here.)
in second setup, is_name
function not applied. creating name
object, regardless, in __init__
method:
class person(object): def __init__(self,s): self.name = name(s.get('name',{})) self.url = s.get('url','')
note self.name = name(...)
line there.
in crazyperson.__init__()
call parent method:
def __init__(self,s): super(crazyperson,self).__init__(s) self.dance = s.get('dance')
passing on s
person.__init__()
creates name()
object.
so when create fred
fred = crazyperson({"name":"fred", ...})
passing name
set string 'fred'
name.__init__()
, expected dictionary instead:
class name(object): def __init__(self,s): self.firstname = s.get('firstname','') self.lastname = s.get('lastname')
and code fails:
>>> 'fred'.get('firstname', '') traceback (most recent call last): file "<stdin>", line 1, in <module> attributeerror: 'str' object has no attribute 'get'
only set name
on person
if no self.name
has been set yet:
class person(object): def __init__(self,s): if not hasattr(self, 'name') self.name = name(s.get('name', {})) self.url = s.get('url','')
and set name
first in crazyperson
:
def __init__(self,s): self.name = s.get('name', 0) self.dance = s.get('dance') super(crazyperson,self).__init__(s)
Comments
Post a Comment