Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/323.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_Python 3.x_Decorator - Fatal编程技术网

带参数的Python装饰器

带参数的Python装饰器,python,python-3.x,decorator,Python,Python 3.x,Decorator,我有一个类具有许多非常相似的属性: class myClass(object): def compute_foo(self): return 3 def compute_bar(self): return 4 @property def foo(self): try: return self._foo except AttributeError:

我有一个类具有许多非常相似的属性:

class myClass(object):

    def compute_foo(self):
        return 3

    def compute_bar(self):
        return 4

    @property
    def foo(self):
        try:
            return self._foo
        except AttributeError:
            self._foo = self.compute_foo()
            return self._foo

    @property
    def bar(self):
        try:
            return self._bar
        except AttributeError:
            self._bar = self.compute_bar()
            return self._bar
    ...   
所以我想我会写一个装饰师来做属性定义的工作

class myDecorator(property):
    def __init__(self, func, prop_name):
        self.func = func
        self.prop_name = prop_name
        self.internal_prop_name = '_' + prop_name

    def fget(self, obj):
        try:
            return obj.__getattribute__(self.internal_prop_name)
        except AttributeError:
            obj.__setattr__(self.internal_prop_name, self.func(obj))
            return obj.__getattribute__(self.internal_prop_name)

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.func is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)


class myClass(object):

    def compute_foo(self):
        return 3
    foo = myDecorator(compute_foo, 'foo')

    def compute_bar(self):
        return 4
    bar = myDecorator(compute_bar, 'bar')
这很好,但是当我想使用
@myDecorator('foo')
语法时,它会变得更复杂,并且无法理解
\uuuuuu调用\uuuuu
方法应该返回什么以及如何将属性附加到其类

目前我有:

class myDecorator(object):
    def __init__(self, prop_name):
        self.prop_name = prop_name
        self.internal_prop_name = '_' + prop_name

    def __call__(self, func):
        self.func = func
        return #???

    def fget(self, obj):
        try:
            return obj.__getattribute__(self.internal_prop_name)
        except AttributeError:
            obj.__setattr__(self.internal_prop_name, self.func(obj))
            return obj.__getattribute__(self.internal_prop_name)

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.func is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

class myClass(object):
    @myDecorator('foo')
    def compute_foo(self):
        return 3

c = myClass()
print(c.foo)

它返回:
AttributeError:“myClass”对象没有属性“foo”

如果要使用
@decorator
语法,则无法将属性重新映射到类上的其他名称。这意味着您的
compute\ux
方法必须重命名为与属性相同的名称

编辑:可以重新映射名称,但也需要使用类装饰器

class MyProperty(property):
    def __init__(self, name, func):
        super(MyProperty, self).__init__(func)
        self.name = name
        self.internal_prop_name = '_' + name
        self.func = func

    def fget(self, obj):
        try:
            return obj.__getattribute__(self.internal_prop_name)
        except AttributeError:
            obj.__setattr__(self.internal_prop_name, self.func(obj))
            return obj.__getattribute__(self.internal_prop_name)

    def __get__(self, obj, objtype=None)
        if obj is None:
            return self
        if self.func is None:
            raise AttributeError('unreadable')
        return self.fget(obj)

def myproperty(*args)
    name = None
    def deco(func):
        return MyProperty(name, func)

    if len(args) == 1 and callable(args[0]):
        name = args[0].__name__
        return deco(args[0])
    else:
        name = args[0]
        return deco


class Test(object):

    @myproperty
    def foo(self):
        return 5
def clsdeco(cls):
    for k, v in cls.__dict__.items():
        if isinstance(v, MyProperty) and v.name != k:
            delattr(cls, k)
            setattr(cls, v.name, v)
    return cls


@clsdeco
class Test(...)

    @myproperty('foo')
    def compute_foo(self):
        pass
如果没有类decorator,name参数唯一相关的时间是如果内部变量名与函数名不同,那么

@myproperty('foobar')
def foo(self):
    return 5
它将查找
\u foobar
而不是
\u foo
,但属性名称仍然是
foo

然而,有一种方法可以重新映射属性名,但也必须使用类装饰器

class MyProperty(property):
    def __init__(self, name, func):
        super(MyProperty, self).__init__(func)
        self.name = name
        self.internal_prop_name = '_' + name
        self.func = func

    def fget(self, obj):
        try:
            return obj.__getattribute__(self.internal_prop_name)
        except AttributeError:
            obj.__setattr__(self.internal_prop_name, self.func(obj))
            return obj.__getattribute__(self.internal_prop_name)

    def __get__(self, obj, objtype=None)
        if obj is None:
            return self
        if self.func is None:
            raise AttributeError('unreadable')
        return self.fget(obj)

def myproperty(*args)
    name = None
    def deco(func):
        return MyProperty(name, func)

    if len(args) == 1 and callable(args[0]):
        name = args[0].__name__
        return deco(args[0])
    else:
        name = args[0]
        return deco


class Test(object):

    @myproperty
    def foo(self):
        return 5
def clsdeco(cls):
    for k, v in cls.__dict__.items():
        if isinstance(v, MyProperty) and v.name != k:
            delattr(cls, k)
            setattr(cls, v.name, v)
    return cls


@clsdeco
class Test(...)

    @myproperty('foo')
    def compute_foo(self):
        pass

这将遍历类上的所有属性,找到
MyProperty
实例,并检查集合名称是否与映射名称相同,如果不相同,则将属性重新绑定到传递到
MyProperty
装饰器的名称

始终可以使用wrapps技巧将参数传递给decorator,如下所示:

from functools import wraps

class myDecorator(property):
    def __init__(self, prop_name):
        self.prop_name = prop_name

    def __call__(self, wrappedCall):
        @wraps(wrappedCall)
        def wrapCall(*args, **kwargs):
            klass = args[0]
            result = wrappedCall(*args, **kwargs)
            setattr(klass, self.prop_name, result)
        return wrapCall

class myClass(object):
    @myDecorator('foo')
    def compute_foo(self):
        return 3

c = myClass()
c.compute_foo()
print c.foo    

我最终得到了一个元类,以使子类化更容易。感谢Brendan Abel在这方面的暗示

import types

class PropertyFromCompute(property):

    def __init__(self, func):
        self.func = None
        self.func_name = func.__name__
        self.internal_prop_name = self.func_name.replace('compute', '')

    def fget(self, obj):
        try:
            return obj.__getattribute__(self.internal_prop_name)
        except AttributeError:
            obj.__setattr__(self.internal_prop_name, self.func())
            return obj.__getattribute__(self.internal_prop_name)

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.func is None:
            try:
                self.func =  obj.__getattribute__(self.func_name)
            except AttributeError:
                raise AttributeError("unreadable attribute")
        return self.fget(obj)

class WithPropertyfromCompute(type):

    def __new__(cls, clsname, bases, dct):
        add_prop = {}
        for name, obj in dct.items():
            if isinstance(obj, types.FunctionType) and name.startswith('compute_'):
                add_prop.update({name.replace('compute_',''): PropertyFromCompute(obj)})
        dct.update(add_prop)
        return super().__new__(cls, clsname, bases, dct)


class myClass(object, metaclass=WithPropertyfromCompute):

    def compute_foo(self):
        raise NotImplementedError('Do not instantiate the base class, ever !')

class myChildClass(myClass):

    def compute_foo(self):
        return 4

base = myClass()
try:
    print(base.foo)
except NotImplementedError as e:
    print(e)
print(myClass.foo)
child = myChildClass()
print(child.foo)

我越看它,我就越相信我想要达到的目标是疯狂的。。。
myDecorator
能知道什么是
myClass
向其添加属性吗?除非调用了
\uuuu get\uuuu()
等等。谢谢。如果我理解正确,那么在本例中使用
@myDecorator('foo')
语法是没有意义的。类属性
foo
应该在类定义中定义,而
myDecorator
应该被调用
myCustomProperty
?您不应该将修饰函数作为参数传递给decorator。这可以通过使用包装器来解决。这很有意义。非常感谢你的例子。实际上,我将
compute\u foo
作为一个单独的函数,以便在子类化时能够覆盖它。所以我可能会坚持单独定义class属性。我喜欢类装饰器的想法。那太好了!我假设我需要对测试的所有子类应用相同的装饰器,它才能工作?是的,它需要对所有子类应用。如果使用元类而不是类装饰器,则可以避免添加所有子类。谢谢,我想到了类似的方法,但我不熟悉
functools.wrapps
。我也将对此进行调查。