是否有可能使Python函数像实例一样?

时间:2010-08-11 15:02:34

标签: python function attributes

我知道函数可以有属性。所以我可以做到以下几点:

def myfunc():
    myfunc.attribute += 1
    print(myfunc.attribute)

myfunc.attribute = 1

是否可以通过任何方式使这样的函数表现得好像它是一个实例?例如,我希望能够做到这样的事情:

x = clever_wrapper(myfunc)
y = clever_wrapper(myfunc)
x.attribute = 5
y.attribute = 9
x()   # I want this to print 6 (from the 5 plus increment)
y()   # I want this to print 10 (from the 9 plus increment)

目前,该函数只有一个“实例”,因此attribute只存在一次。通过xy进行修改会更改相同的值。我希望他们每个人都拥有自己的attribute。这有可能吗?如果是这样,你能提供一个简单,实用的例子吗?

重要的是,我能够从函数内部访问attribute,但attribute的值不同,具体取决于调用函数的“实例”。本质上,我想使用attribute,就好像它是函数的另一个参数(这样它可以改变函数的行为)但不传递它。(假设函数的签名是固定的这样我就无法更改参数列表。)但我需要能够为attribute设置不同的值,然后按顺序调用这些函数。我希望这是有道理的。

主要答案似乎是要做这样的事情:

class wrapper(object):
    def __init__(self, target):
        self.target = target
    def __call__(self, *args, **kwargs):
        return self.target(*args, **kwargs)

def test(a):
    return a + test.attribute

x = wrapper(test)
y = wrapper(test)
x.attribute = 2
y.attribute = 3
print(x.attribute) 
print(y.attribute)
print(x(3))
print(y(7))

但这不起作用。也许我做错了,但它说test没有attribute。 (我假设这是因为wrapper实际上有属性。)

我需要这个的原因是因为我有一个库需要一个具有特定签名的函数。可以将这些函数放入各种管道中,以便按顺序调用它们。我想传递它的相同功能的多个版本,但是根据属性的值改变它们的行为。所以我希望能够将xy添加到管道中,而不是必须实现test1函数和test2函数完全相同的事情(属性的值除外)。

5 个答案:

答案 0 :(得分:7)

您可以使用__call__方法创建一个可以实现类似功能的类。

为了清晰起见而编辑而不是使myfunc成为一个函数,请将其设为可调用类。它像一个函数一样行走,它像一个函数一样嘎嘎叫,但它可以像一个类一样有成员。

答案 1 :(得分:2)

更好的方式:

def funfactory( attribute ):
    def func( *args, **kwargs ):
        # stuff
        print( attribute )
        # more stuff

    return func

x = funfactory( 1 )
y = funfactory( 2 )

x( ) # 1
y( ) # 2

这是因为函数是闭包,所以它们将获取其范围内的所有局部变量;这会导致attribute的副本与函数一起传递。

答案 2 :(得分:1)

class Callable(object):
    def __init__(self, x):
        self.x = x

    def __call__(self):
        self.x += 1
        print self.x

>> c1 = Callable(5)
>> c2 = Callable(20)
>> c1()
6
>> c1()
7
>> c2()
21
>> c2()
22

答案 3 :(得分:0)

#!/usr/bin/env python
# encoding: utf-8

class Callable(object):
    attribute = 0

    def __call__(self, *args, **kwargs):
        return self.attribute

def main():
    c = Callable()
    c.attribute += 1
    print c()


if __name__ == '__main__':
    main()

答案 4 :(得分:0)

生成器可能是另一种解决方案:

def incgen(init):
    while True:
        init += 1
        print init
        yield

x = incgen(5)
y = incgen(9)

x.next() # prints 6
y.next() # prints 10
y.next() # prints 11
x.next() # prints 7

你不能重新使用生成器并操纵数据。