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

Python 如何区分装饰器中的方法和函数?

Python 如何区分装饰器中的方法和函数?,python,decorator,Python,Decorator,我想编写一个decorator,根据它是应用于函数还是应用于方法,它的行为会有所不同 def some_decorator(func): if the_magic_happens_here(func): # <---- Point of interest print 'Yay, found a method ^_^ (unbound jet)' else: print 'Meh, just an ordinary function :/'

我想编写一个decorator,根据它是应用于函数还是应用于方法,它的行为会有所不同

def some_decorator(func):
    if the_magic_happens_here(func): # <---- Point of interest
        print 'Yay, found a method ^_^ (unbound jet)'
    else:
        print 'Meh, just an ordinary function :/'
    return func

class MyClass(object):
    @some_decorator
    def method(self):
        pass

@some_decorator
def function():
    pass
def some_decorator(func):

如果这里发生了(func):#我将依赖于这样一种约定,即将成为方法的函数有一个名为
self
的第一个参数,而其他函数没有。脆弱,但是,没有真正坚实的道路

因此(伪代码,因为我有注释来代替您在这两种情况下想要做的事情…):

一种让它更可靠的方法,如您所说,所有涉及的类都从您控制的类继承,就是让该类提供一个元类(当然也将由所述类继承),它检查类主体末尾的内容。使包装函数可访问,例如通过
wrapper.\u f=f
和元类的
\uuu init\uuu
可以检查所有包装方法是否确实将
self
作为第一个参数

不幸的是,没有简单的方法可以检查正在包装的其他函数(非未来方法)是否没有这样的第一个参数,因为在这种情况下,您无法控制环境。修饰符可以通过函数的
f_globals
(globals dict,即模块的dict)和
f_name
属性检查“顶级”函数(其
def
在其模块中是顶级语句的函数)——如果该函数是这样一个全局函数,那么它以后可能不会被指定为类的属性(因此无论如何都将成为未来的方法;-)因此名为first arg的
self
如果存在,可以被诊断为错误并发出警告(同时仍将函数视为实际函数;-)

另一种方法是,在实函数的假设下,在装饰器本身中进行装饰,但也可以将原始函数对象作为
包装器使用。\u f
。然后,元类的
\uuu init\uuu
可以对它所看到的类体中以这种方式标记的所有函数重新进行装饰这种方法比我刚才画的那种依赖惯例的方法更为可靠,即使有额外的检查

class Foo(Bar): ... # no decorations

@decorator
def f(*a, **k): ...

Foo.f = f   # "a killer"... function becomes method!
仍然会有问题--您可以尝试在元类中使用
\uuuu setattr\uuuu
截取此消息(但是
class
语句之后对类属性的其他赋值可能会有问题)


用户的代码越能自由地做一些奇怪的事情(Python通常会给程序员留下很多这样的自由),当然,你的“framework-y”代码越难严格控制事情;-)。

你需要在选择返回哪个包装器的地方变魔术吗,或者你可以推迟到函数真正被调用时再使用魔法吗? 您可以尝试向您的decorator输入一个参数,以指示它应该使用两个包装器中的哪一个,如

def some_decorator( clams ):
   def _mydecor(func ):
       @wraps(func)
       def wrapping(*args....)
          ...
       return wrapping
   def _myclassdecor(func):
       @wraps(func)
       .....

   return _mydecor if clams else _myclassdecor

我建议的另一件事是创建一个元类,并在元类中定义init方法,以查找用修饰符修饰的方法,并相应地修改它们,就像Alex暗示的那样。在基类中使用这个元类,因为所有使用decorator的类都将从基类继承,所以它们也将获得元类类型并使用它的init

您只需检查正在修饰的函数是否具有
im\u func
属性。如果是,那么它就是一种方法。如果没有,那么它就是一个函数

注意下面的代码示例在调用时进行检测,但您也可以在装饰时进行检测。只需将
hasattr
检查移动到外部装饰生成器

Python 2.6.4 (r264:75706, Dec  7 2009, 18:45:15) 
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def deco(f):
...   def _wrapper(*args, **kwargs):
...     if hasattr(f, 'im_func'):
...       print 'method'
...     else:
...       print 'function'
...   return _wrapper
... 
>>> deco(lambda x: None)()
function
>>> def f(x):
...   return x + 5
... 
>>> deco(f)()
function
>>> class A:
...   def f(self, x):
...     return x + 5
... 
>>> a = A()
>>> deco(a.f)()
method
>>> deco(A.f)()
method
>>> 
编辑

哦,快!我完全错了。所以我应该更彻底地阅读亚历克斯的帖子

>>> class B:
...   @deco
...   def f(self, x):
...     return x +5
... 
>>> b = B()
>>> b.f()
function

从Python 3.3开始,使用:


A
的方法
x
将有一个
\uuuuqualname\uuu
,即
A.x
,而函数
x
将有一个
\uuuuqualname\uuu

\uuuuuuuuuqualname\ucode>,元类方法做到了,谢谢!我可以在定义时撤消由
元类找到的方法的decorator的操作。\uuuuu init\uuuu
并在
基类中重新附加decorator。初始化后,这次使用绑定实例方法而不是即将成为方法的unbound函数。我认为应该是
args=inspect.getargspec(f).args
仅供将来读者参考:该代码已被弃用,且该方法已过时。(显然这篇文章已经很老了。)使用@kentwait的方法。元类方法做到了:)你的解决方案有效,但我只能“接受”一个答案,Alex是第一个,对不起。Stackoverflow应该允许“接受”多个答案…现代方法!
>>> class B:
...   @deco
...   def f(self, x):
...     return x +5
... 
>>> b = B()
>>> b.f()
function
def some_decorator(func):
    if func.__name__ != func.__qualname__:
        print('Yay, found a method ^_^ (unbound jet)')
    else:
        print('Meh, just an ordinary function :/')
    return func