gpt4 book ai didi

python - 重写修饰的子类方法

转载 作者:太空宇宙 更新时间:2023-11-03 18:54:31 24 4
gpt4 key购买 nike

我正在摆弄继承,发现了一种对我来说似乎很奇怪的行为——也就是说,有时我可以重写父装饰器函数(用于验证),但有时我不能,而且我无法理解为什么或者有什么区别。

文字快速演练——我有一个人对象,我想将其子类化为更特定的人对象。更具体的一个将有一个附加字段“舞蹈”,并且对前一个字段“名称”将具有不同的验证规则。

这是我有效的基本案例:

# 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'

看起来它已经在字符串名称“Fred”上调用了 Name 初始值设定项:Name(s.get('name',{}))

但似乎不可能,因为在前面的示例中,我能够删除完全矛盾的验证(is_stris_not_str)。为什么这相反较少但失败较多?在第一种情况下,它没有同时应用 is_stris_not_str,为什么现在/同时应用 is_Nameis_str 具有看似相同的语法?

我的问题是:第一种方法与第二种方法有什么不同,导致它成功?我试图在这里隔离变量,但不明白为什么我可以撤消场景一中从父类继承的包装验证器,但不能做场景二中看起来类似的事情。似乎唯一有意义的区别是它是一个对象而不是字符串。

(我知道,更好的架构方法来做到这一点是有第三个更抽象的父类,没有需要更改的验证规则——两种人都会继承它。但我也明白我我应该能够更改子类中的方法,所以我想至少了解一个成功和另一个失败的原因之间的区别。)

最佳答案

在您的第二个设置中,is_Name函数应用。您正在创建Name对象,无论如何,在 __init__方法:

class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')

注意 self.name = Name(...)在那儿排队。

Crazyperson.__init__()您调用父方法:

def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.dance = s.get('dance')

传递sPerson.__init__()这会创建一个 Name()对象。

所以当你创建fred时与 fred = Crazyperson({"name":"Fred", ...})你正在路过name设置为字符串 'Fred'Name.__init__() ,它需要一个字典:

class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')

这就是您的代码失败的地方:

>>> 'Fred'.get('firstname', '')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'

仅设置namePerson如果没有self.name已设置:

class Person(object):
def __init__(self,s):
if not hasattr(self, 'name')
self.name = Name(s.get('name', {}))
self.url = s.get('url','')

并设置name 第一 Crazyperson :

def __init__(self,s):
self.name = s.get('name', 0)
self.dance = s.get('dance')
super(Crazyperson,self).__init__(s)

关于python - 重写修饰的子类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17597776/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com