python - Overriding decorated subclass methods -


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