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

Python 装饰方法:属性错误:';功能';对象没有属性'__自我&';?

Python 装饰方法:属性错误:';功能';对象没有属性'__自我&';?,python,python-3.x,python-decorators,Python,Python 3.x,Python Decorators,我正在使用asyncio来调度在特定相对时间间隔调用的方法。我决定将调度集中到我编写的类的一个方法中,以减少项目逻辑出错的机会 每次计划的方法完成时都应该调用这样的方法。我想在每个方法的末尾添加loop.call_soon,但我决定尝试一下 我编写了一个类装饰器,然后将其应用到我的主类的一些方法中,编写了其余的逻辑以及所有这些。但是,当尝试在我的项目上测试我的更改时,我遇到了一个异常: AttributeError: 'function' object has no attribute '__s

我正在使用
asyncio
来调度在特定相对时间间隔调用的方法。我决定将调度集中到我编写的类的一个方法中,以减少项目逻辑出错的机会

每次计划的方法完成时都应该调用这样的方法。我想在每个方法的末尾添加
loop.call_soon
,但我决定尝试一下

我编写了一个类装饰器,然后将其应用到我的主类的一些方法中,编写了其余的逻辑以及所有这些。但是,当尝试在我的项目上测试我的更改时,我遇到了一个异常:

AttributeError: 'function' object has no attribute '__self__'
不知怎的,装饰我的方法使它成为一种功能。这是我无法理解的,为什么会发生这种情况?我如何在不放弃装饰师的情况下解决这个问题

以下是我尝试做的一个最小、完整且可验证的示例:

import asyncio
from datetime import datetime


class thinkagain:
    loop = asyncio.get_event_loop()

    def __init__(self, f):
        self.fun = f
        self.class_ = f.__self__

    def __call__(self):
        self.fun(*args, **kwords)
        # everything in Python is an object
        setattr(self.fun, "called", datetime.utcnow())
        self.loop.call_later(self.class_.think, 5 * 60)


class DoSomething:
    loop = asyncio.get_event_loop()

    @thinkagain
    def think(self):
        attr = getattr(self.dosomething, "called")
        if attr:
            elapsed = attr - datetime.utcnow()
            seconds = elapsed.seconds
        else:
            seconds = 99999

        if seconds >= 20 * 60:
            self.loop.call_soon(self.dosomething)

    @thinkagain
    def dosomething(self):
        print("I did something awesome!")

loop = asyncio.get_event_loop()
something = DoSomething()
loop.call_soon(something.think)
loop.run_forever()
以下是我得到的一个例外:

Python 3.5.1 (default, Dec  7 2015, 13:41:59) 
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/mcve.py", line 19, in <module>
    class DoSomething:
  File "/tmp/mcve.py", line 22, in DoSomething
    @thinkagain
  File "/tmp/mcve.py", line 10, in __init__
    self.class_ = f.__self__
AttributeError: 'function' object has no attribute '__self__'
>>> 
Python 3.5.1(默认,2015年12月7日13:41:59)
linux上的[GCC 5.2.0]
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“/tmp/mcve.py”,第19行,在
类剂量:
文件“/tmp/mcve.py”,第22行,剂量测定法
@再想想
文件“/tmp/mcve.py”,第10行,在初始化中__
self.class=f.\uu self__
AttributeError:“函数”对象没有属性“\uuuu self\uuuu”
>>> 
不知怎的,装饰我的方法使它成为一种功能

不对。函数被创建,然后被修饰,然后成为一个方法。您将需要编写一个包装器,在运行时捕获
self
参数,然后调用实际函数。

关于装饰器,我们做了一个精彩的演讲 ,讨论各种装饰风格和技术的内部实现。强烈推荐

他最后介绍了相关模块:

尽管如此,我还是用两个版本修改了您的示例。 下面的版本将属性直接存储在您想要的方法中

from datetime import datetime

class thinkagain:

    def __init__(self, f):
        # Plain function as argument to be decorated
        self.func = f

    def __get__(self, instance, owner):
        self.instance_ = instance
        return self.__call__

    def __call__(self, *args, **kwargs):
        """Invoked on every call of any decorated method"""

        # set attribute directly within bound method
        bound_method = getattr(self.instance_, self.func.__name__)
        bound_method.__dict__['called'] = datetime.utcnow()

        # returning original function with class' instance as self
        return self.func(self.instance_, *args, **kwargs)


class DoSomething_A:

    @thinkagain
    def think(self, *args, **kwargs):
        print('\n%s' % locals())
        print(self.think.called, args, kwargs)
        self.dosomething()

    @thinkagain
    def dosomething(self):
        print('%s\n' % ('-'*30), locals())
        print("%s I did something awful" % self.dosomething.called)
第二个版本看起来更干净,它跳过了在方法中存储属性,并直接在实例中分配属性

from datetime import datetime

class thinkagain:

    def __init__(self, f):
        # Plain function as argument to be decorated
        self.func = f

    def __get__(self, instance, owner):
        self.instance_ = instance
        return self.__call__

    def __call__(self, *args, **kwargs):
        """Invoked on every call of decorated method"""

        # set attribute on instance
        name = '%s_called' % self.func.__name__
        setattr(self.instance_, name, datetime.utcnow())

        # returning original function with class' instance as self
        return self.func(self.instance_, *args, **kwargs)


class DoSomething_B:

    @thinkagain
    def think(self, *args, **kwargs):
        print('\n%s' % locals())
        print(self.think_called)
        self.dosomething()

    @thinkagain
    def dosomething(self):
        print('%s\n' % ('-'*30), locals())
        print(self.dosomething_called)
两者产生相同的期望行为:

>>> something = DoSomething_A().think(1, 2)
{'args': (1, 2), 'kwargs': {}, 'self': <__main__.DoSomething_A object at     0x10209f128>}
2015-12-26 04:13:25.629887 (1, 2) {}
------------------------------
{'self': <__main__.DoSomething_A object at 0x10209f128>}
2015-12-26 04:13:25.647476 I did something awful
思考(1,2) {'args':(1,2),'kwargs':{},'self':} 2015-12-26 04:13:25.629887 (1, 2) {} ------------------------------ {'self':} 2015-12-26 04:13:25.647476我做了件可怕的事 及

something=DoSomething\u B()。思考('arg\u a','arg\u B')) {'args':('arg_a','arg_b'),'kwargs':{},'self':} 2015-12-26 04:13:25.648039 ------------------------------ {'self':} 2015-12-26 04:13:25.648390
«然后它就变成了一个方法。»属于哪一类?类的
thinkreach
类或
DoSomething
class?,该类定义了
class
块。函数和方法是相同的。函数有一个描述符\uuuu get\uuuuu()方法,允许它们按方法的方式使用。@JacobZimmerman:不,它们不是。函数只有在从实例访问时才成为方法。@IgnacioVazquez Abrams它们只有在以这种方式使用时才会像方法一样工作,但它们上始终有_get__;()(描述符协议的一部分),这允许它们以这种方式工作。相信我,我正在写一本关于描述符的书。如果您指的是成为“方法”对象,那么您是对的,但这只是一个专门的
partial
函数,它具有
self
pre-setNote,这可能不是您真正想要做的。考虑一个修饰的方法是否在类的不同实例中调用相同的方法(或者在一个线程上下文中……方法可以在不同的实例上并行调用)——现在你进入一个非常混乱的状态,其中<代码> Surviv.Stase是指上次调用方法的实例。话虽如此,这是一个不错的例子——我只是不希望人们在不了解其后果的情况下复制/粘贴此代码。为什么在使用类装饰器时传递给
thinkreach.\uuuu get\uuuuuuuu
的参数没有被传递进来,比如说,
thinkreach.\uuuu init\uuuuuuuuuuuu
因为装饰器的
@
只是
func=decorator(dec_args)(func)(args)
的语法糖,如果装饰器有自己的参数,并且
func=decorator(func)(args)
如果没有。
>>> something = DoSomething_B().think('arg_a', 'arg_b')
{'args': ('arg_a', 'arg_b'), 'kwargs': {}, 'self': <__main__.DoSomething_B object at 0x10209f208>}
2015-12-26 04:13:25.648039
------------------------------
{'self': <__main__.DoSomething_B object at 0x10209f208>}
2015-12-26 04:13:25.648390