在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__
的实例化,或者只有一个实例?
答案 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
的每个实例,_func
和Obj
都保持不变,唯一不同的是使用的实例。
试试这个:
>>> 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总是创建一个新的。