python对象是单例函数吗?

时间:2011-09-18 23:05:53

标签: python

在Java中,对象使用链接到它们的类的函数的完整副本进行实例化。这是Spring Framework如此成功的原因之一。 Spring可以帮助您减少Java VM在创建许多临时数据对象时使用的内存,以及Spring提供的其他服务对象作为有效承载所有功能的单例。

我只是想知道Python中是否真的如此?好像不是。但这意味着,如果你对某个对象的 dict 一塌糊涂,那么你正在改变该类所有副本的所有功能,对吗?

例如:

class MyObj:
  a = 23

  def __init__(self, b):
     self.b = b

  def __add__(self, c):
     return self.b + c

如果我创建一个MyObj的数组,是否有一个__add__的实例化,或者只有一个实例?

3 个答案:

答案 0 :(得分:6)

只有一个函数实例存储在类中。该函数通过引用类实例作为第一个参数来调用,该参数传统上称为self

因此,以下是调用.__add__()方法函数的三种等效方法:

>>> x = MyObj(2)
>>> MyObj.__add__(x, 3)  # we pass in a reference to x explicitly
5
>>> x.__add__(3)  # method call implicitly passes reference to x
5
>>> x + 3  # overloaded operator implicitly passes reference to x
5

此外,在Python中,函数的“类型签名”不是函数名称的一部分。您获得的唯一.__add__()方法是您声明的方法。在Python中,您只需编写代码,使一个函数执行您可能希望它执行的所有不同作业。例如,如果您希望.__add__()将字符串转换为整数以使x + "3"返回5,那么您可以这样做:

class MyObj(object): # see note 0
    a = 23
    def __init__(self, b):
        self.b = b
    def __add__(self, other):
        return self.b + int(other)  # see note 1
    def a_add(self, other):
        return MyObj.a + other

注意0:最好将您的类声明为继承自object。在Python 3.x中,无论您是否以这种方式声明它,您的类都将继承自object,但在Python 2.x中,除非您使用{{1}声明它,否则将获得“旧式类”在课名之后。

注1:我们不打算检查(object)参数的类型;我们只是尝试将其强制为other值。这是“鸭子打字”的行动。现在不仅将字符串强制转换为整数,而且可以成功强制转换为int的任何内容都可以正常工作。

有时,要使一个函数执行多个作业,您可能需要额外的参数。您可以将它们设置为可选,这样您每次调用该函数时都不需要指定它们。您可以在Python教程中阅读更多相关内容;这是一个地方:http://diveintopython.net/power_of_introspection/optional_arguments.html

最后,在Python中,您需要明确使用int来引用该类变量MyObj.a。如果你只是在成员函数中使用a,你将获得通常的名称解析规则,它将在全局命名空间中查找;对象命名空间并不特殊。

你也可以这样得到一个类变量:

a

但这只有在继承自def a_add(self, other): cls = type(self) # cls will be set to the class return cls.a + other 的新式类时才有效!

答案 1 :(得分:3)

Java不为每个实例创建新函数。事实上,Java has no function objects开头。

另一方面,Python具有一流的功能。从类的实例获取函数对象会将类函数包装在名为bound method的简单对象中。调用bound method时,将实例对象作为第一个参数调用底层类函数。

你可以说给定这个对象

class Obj:
    def f(self):
        print self
o = Obj()

当你做

f = o.f # bound method
发生了这种情况

import types
_func = Obj.__dict__['f'] # get the raw function
f = types.MethodType(_func, o, Obj) # create a "bound method"

正如您所看到的,对于Obj的每个实例,_funcObj都保持不变,唯一不同的是使用的实例。

试试这个:

>>> o.f
<bound method Obj.f of <__main__.Obj instance at 0x0222CAD0>>
>>> Obj.f
<unbound method Obj.f>
>>> o.f() # this is equivalent to:
>>> Obj.f(o)

答案 2 :(得分:1)

这里似乎有几个问题。我将尝试回答释义版本,以便更清楚地了解我的回应。

  

[实例上的方法属性与类相比有哪些开销?]

在python中,几乎所有类型的容器,从局部变量到类属性,都是对其他对象的引用。定义函数时,将创建一次函数值,并使用引用将变量初始化为函数。如果一个函数在类定义的范围内,python会将它添加到类的属性中,但它仍然是对原始函数的引用。

因此,除了引用成本之外,没有真正的方法可以对函数进行任何开销。此外,类中的属性仅间接关联(通过对象的__class__属性),因此类上的 number 属性不会在实例上带来额外成本。

  

[我可以修改一个类实例的方法吗?我可以修改所有实例的方法吗?]

对象的属性位于该对象的__dict__中(正如您已经注意到的),这适用于类型的属性(也是对象)。在解析属性时,python采用相当一致的搜索模式;首先它检查类中的描述符,然后检查实例的__dict__,然后检查类__dict__;它挑选了它找到的第一个。一个相当明显的例子可能如此:

class Foo(object):
    bar = "apples"

my_foo = Foo()
your_foo = Foo()
your_foo.bar = "bananas"
my_foo.__class__.bar = "oranges"
# your_foo.bar is still "bananas"

但是有一些方法可以解决问题。当你得到一个恰好是函数的class属性时,python会动态地将它变成instancemethod,它将self参数绑定到实例(如果从类中获取属性,则不会) 。仅当属性取自而非实例时才会出现此效果。如果要使用新方法修改实例,则必须自己将该函数转换为实例方法。您可以使用types.MethodType,但我认为应该使用functools.partial处理此案例:

import functools
def my_method(self):
    "do something clever"

my_foo.baz = functools.partial(my_method, my_foo)
  

是否有一个__add__的实例化,或者只有一个实例?

了解实际问题还有很长的路要走。但总而言之,__add__方法的函数只出现一次。 __dict__ MyObj中只有一个引用它。可能有许多实例的instancemethod将该函数包装到方法中;事实上,每次进行查找时都会有一个; python总是创建一个新的。

相关问题