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

Python 调用保存在类属性中的函数:内置函数与普通函数的不同行为

Python 调用保存在类属性中的函数:内置函数与普通函数的不同行为,python,python-2.7,python-internals,Python,Python 2.7,Python Internals,使用以下脚本: import time class Foo(object): func = time.gmtime def go(self): return self.func(0.0) print time.strftime('%Y', Foo().go()) 我得到以下输出: 1970 但是,如果我稍作修改并包装time.gmtime: import time def tmp(stamp): return time.gmtime(stamp)

使用以下脚本:

import time

class Foo(object):
    func = time.gmtime
    def go(self):
        return self.func(0.0)

print time.strftime('%Y', Foo().go())
我得到以下输出:

1970
但是,如果我稍作修改并包装
time.gmtime

import time

def tmp(stamp):
    return time.gmtime(stamp)

class Foo(object):
    func = tmp
    def go(self):
        return self.func(0.0)

print time.strftime('%Y', Foo().go())
然后我得到以下错误:

回溯(最近一次呼叫最后一次):
文件“/test.py”,第13行,在
打印时间.strftime(“%Y”,Foo().go())
文件“/test.py”,第11行,在go中
返回self.func(0.0)
TypeError:tmp()正好接受1个参数(给定2个)
显然,它试图调用
Foo.func
,就好像它是一个实例方法,并将
self
作为第一个参数传递

两个问题:

  • time.gmtime
    tmp
    都是采用单个参数的函数,那么为什么这两个脚本的行为不同呢
  • 如何安全地将普通函数保存在类/实例属性中,并在以后从实例方法调用它

如果希望将函数存储为实例属性:

class Foo(object):
    def __init__(self):
        self.func = tmp

    def go(self):
        return self.func(0.0)
您需要在实例的上下文中分配函数,在初始值设定项中这样做是很可能的


希望这能有所帮助。

请注意其中的区别…:

>>> import time
>>> type(time.gmtime)
<type 'builtin_function_or_method'>
>>> def f(*a): return time.gmtime(*a)
... 
>>> type(f)
<type 'function'>
导入时间 >>>类型(time.gmtime) >>>def f(*a):返回时间.gmtime(*a) ... >>>类型(f) 一个
(它是一个描述符)与一个
(它不是)

您会问“如何安全地将普通函数保存在类/实例属性中,并在以后从实例方法调用它?”

如果“普通函数”是指
,则将其包装为
静态方法
,以避免调用时将
self
作为第一个参数插入。而且,也可以将
staticmethod
封装在内置的方法周围。所以我想这是你最好的选择

  • time.gmtime
    tmp
    都是采用单个参数的函数,那么为什么这两个脚本的行为不同呢
  • 让我们了解Python在执行此操作时的功能

    self.func
    

    当引用的实例属性不是数据属性时,将搜索其类。如果名称表示函数对象的有效类属性,则通过打包(指向)实例对象和在抽象对象中刚刚找到的函数对象来创建方法对象:这是方法对象。使用参数列表调用方法对象时,将从实例对象和参数列表构造新的参数列表,并使用此新参数列表调用函数对象

    因此,如果
    func
    self
    中的有效函数对象,则将创建一个绑定方法对象,该对象期望第一个参数是调用此函数的对象

    现在我们来比较一下,

    print type(time.gmtime)
    
    class Foo(object):
        func = time.gmtime
        def go(self):
            print type(self.func)
            return self.func(0.0)
    
    当使用
    Foo().go()
    调用
    go
    时,它将打印

    <type 'builtin_function_or_method'>
    <type 'builtin_function_or_method'>
    
    将打印

    <type 'function'> <function tmp at 0x7f34d9983578>
    <type 'instancemethod'> <bound method Foo.tmp of <__main__.Foo object at ...>>
    
    这是有效的

    tmp(self, 0.0)
    
    这就是为什么会出现以下错误

    TypeError:tmp()正好接受1个参数(给定2个)
    
  • 如何安全地将普通函数保存在类/实例属性中,并在以后从实例方法调用它
  • 解决方案1:

    再次引用,

    还需要注意的是,作为类实例属性的用户定义函数不会转换为绑定方法;只有当函数是类的属性时才会发生这种情况。

    这意味着,当您将用户定义的函数分配给类变量时,将发生绑定方法构造。但是,如果将其分配给实例,则不会发生

    所以,你可以利用这个优势,像这样

    Foo.func(self, 0.0)
    
    import time
    
    def tmp(stamp):
        return time.gmtime(stamp)
    
    class Foo(object):
        def __init__(self):
            self.func = tmp       # Instance variable, not a class variable
    
        def go(self):
            return self.func(0.0)
    
    print time.strftime('%Y', Foo().go())
    
    class Foo(object):
        func = staticmethod(tmp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    
    class Foo(object):
    
        @staticmethod
        def func(stamp):
            return time.gmtime(stamp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    
    这里,
    self.func
    将转换为
    tmp(0.0)
    ,因为没有发生绑定方法构造

    解决方案2:

    使用这样的函数

    Foo.func(self, 0.0)
    
    import time
    
    def tmp(stamp):
        return time.gmtime(stamp)
    
    class Foo(object):
        def __init__(self):
            self.func = tmp       # Instance variable, not a class variable
    
        def go(self):
            return self.func(0.0)
    
    print time.strftime('%Y', Foo().go())
    
    class Foo(object):
        func = staticmethod(tmp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    
    class Foo(object):
    
        @staticmethod
        def func(stamp):
            return time.gmtime(stamp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    
    现在,
    self.func
    仍然会引用
    tmp
    。类似于这样定义类

    Foo.func(self, 0.0)
    
    import time
    
    def tmp(stamp):
        return time.gmtime(stamp)
    
    class Foo(object):
        def __init__(self):
            self.func = tmp       # Instance variable, not a class variable
    
        def go(self):
            return self.func(0.0)
    
    print time.strftime('%Y', Foo().go())
    
    class Foo(object):
        func = staticmethod(tmp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    
    class Foo(object):
    
        @staticmethod
        def func(stamp):
            return time.gmtime(stamp)
    
        def go(self):
            # `return Foo.func(0.0)` This will also work
            return self.func(0.0)
    

    我怀疑它会有帮助:在
    func(0.0)
    调用时,您会收到
    namererror
    。确实,谢谢。删除了第二个代码段。如果需要的话,第一种方法是有效的。