Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 重写修饰子类方法_Python_Validation_Subclass_Wrapper_Decorator - Fatal编程技术网

Python 重写修饰子类方法

Python 重写修饰子类方法,python,validation,subclass,wrapper,decorator,Python,Validation,Subclass,Wrapper,Decorator,我在处理继承时发现了一种对我来说似乎很奇怪的行为——即,有时我可以覆盖父装饰函数(用于验证),但有时我不能,我无法理解为什么或者区别是什么 简单介绍一下words——我有一个person对象,我想将其子类化为一个更特殊的person对象。更具体的一个将有一个额外的字段“Dance”,并在前面的字段“name”上有不同的验证规则 以下是我的基本案例: # Define the validation wrapper def ensure(name, validate, doc=None):

我在处理继承时发现了一种对我来说似乎很奇怪的行为——即,有时我可以覆盖父装饰函数(用于验证),但有时我不能,我无法理解为什么或者区别是什么

简单介绍一下words——我有一个person对象,我想将其子类化为一个更特殊的person对象。更具体的一个将有一个额外的字段“Dance”,并在前面的字段“name”上有不同的验证规则

以下是我的基本案例:

# 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的创建方式确保验证
成功。如果你试图在那里输入一个数字,它就会失败。同样,第二个对象接受非字符串,因此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失败。该错误表明,
\uuu init\uuu
中的
is\u 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'
回溯(最近一次呼叫最后一次):
文件“”,第3行,在
文件“”,第4行,在_init中__
文件“”,第5行,在_init中__
文件“”,第5行,在_init中__
AttributeError:“str”对象没有属性“get”
它似乎调用了字符串名“Fred”上的
Name
初始值设定项:
Name(s.get('Name',{}))

但似乎不可能,因为在前面的示例中,我能够删除一个完全矛盾的验证(
is\u str
is\u str
)。为什么这一点相对较少,但失败更多?在第一种情况下,它没有同时应用
is_str
is_str
,为什么它/现在/同时应用
is_Name
is_str

我的问题是:第一种方法与第二种方法有什么不同之处?我试图在这里隔离变量,但不明白为什么我可以撤消场景I中从父类继承的包装验证程序,但无法执行场景II中类似的操作。看起来唯一有意义的区别是它是一个对象而不是一个字符串


(我知道更好的体系结构方法是拥有第三个更抽象的父类,不需要更改验证规则——这两种类型的人都会从中继承。但我也知道我应该能够更改子类中的方法,所以我想至少了解其中一个成功,另一个失败。)

在第二次设置中,未应用
is\u Name
功能。您正在创建
Name
对象,不管怎样,在
初始化方法中:

class Person(object):
    def __init__(self,s):
        self.name = Name(s.get('name',{}))
        self.url = s.get('url','')
注意那里的
self.name=name(…)

Crazyperson.\uuuu init\uuuu()
中调用父方法:

def __init__(self,s):
    super(Crazyperson,self).__init__(s)
    self.dance = s.get('dance') 
s
传递给
Person.\uuu init\uuuu()
创建
Name()
对象

因此,当您使用
fred=Crazyperson({“name”:“fred”,…})
创建
fred
时,您将
name
设置为字符串
'fred'
传递给
name.\uu init\uuu()
,这需要一个字典:

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'
并在
Crazyperson
中首先设置
name

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

在第二次设置中,未应用
is\u Name
函数。无论如何,在
\u init\u
方法中,您正在创建
Name
对象:

class Person(object):
    def __init__(self,s):
        self.name = Name(s.get('name',{}))
        self.url = s.get('url','')
注意那里的
self.name=name(…)

Crazyperson.\uuuu init\uuuu()
中调用父方法:

def __init__(self,s):
    super(Crazyperson,self).__init__(s)
    self.dance = s.get('dance') 
s
传递给
Person.\uuu init\uuuu()
创建
Name()
对象

因此,当您使用
fred=Crazyperson({“name”:“fred”,…})
创建
fred
时,您将
name
设置为字符串
'fred'
传递给
name.\uu init\uuu()
,这需要一个字典:

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'
并在
Crazyperson
中首先设置
name

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

我明白了,这是有道理的---对不起,这是我犯的一个不同的错误,但我仍然使用了相同的错误。我已经更正了它,因此我有了一个新的init方法作为name。但是,它显然仍然首先调用原始方法并给我相同的错误。我如何在场景II中初始化init/为n初始化这个单一变量ame/在第二种情况下有所不同?(无需重写init的“url”部分,两者都是通用的?)很抱歉,我一点也不清楚在场景II中调用了错误的方法。什么是回溯?您可能希望使用该信息更新您的问题。您可以将名称设置为可选名称。如果
s
中没有
name
键,则根本不设置
self.name
。然后在
Crazyperson
中设置name,从
s
中删除密钥,然后继续。抱歉,刚刚发布了更新。添加的新行位于Crazyperson的init下
self.name=s.get('name','')#这是密钥
。但是,运行时,我仍然会得到:
回溯(上次调用):文件“”,第3行,在文件“”中,第4行,在uu init文件“”中,第5行,在uuu init_uuuu文件“”中,第5行,在uuu init_uuu属性错误:“str”对象没有属性“get”
当然有。您仍然调用
Person.uuu init_uuuuu()
第一行。行
超级(Crazyperson,self)。\uu init(s)
首先运行,调用
Person.\uuuu init(s)
调用
self.name('name'){/code>
找到字符串等。我明白了,这是有道理的——抱歉,这是我犯的另一个错误,但仍然适用于此。我已经更正了它,因此我有了一个新的init方法作为name。但是,它显然仍在调用