__new__ and __init__ in Python
我正在学习python,到目前为止,我可以告诉你以下关于__new__和__init__的事情:
__new__用于对象创建
__init__用于对象初始化
__new__在__init__之前被调用,因为__new__返回一个新实例,__init__随后被调用以初始化内部状态。
__new__适用于不可变对象,因为一旦分配,它们就不能更改。所以我们可以返回具有新状态的新实例。
我们可以用__new__和__init__来表示两个可变对象,因为它的内部状态是可以改变的。
但我现在还有一个问题。
当我创建一个新实例,如a = MyClass("hello","world")时,这些参数是如何传递的?我的意思是,我应该如何使用__init__和__new__构造类,因为它们不同,并且除了默认第一个参数外,都接受任意参数。
self关键字是在名称方面可以改成别的什么吗?但我想知道,cls是不是在名称方面会发生变化,因为它只是一个参数名?
我做了如下的一些实验:
1 2 3
| >>> class MyClass(tuple):
def __new__(tuple):
return [1,2,3] |
我做了以下的事情:
1 2 3
| >>> a = MyClass()
>>> a
[1, 2, 3] |
虽然我说我要返回tuple,但这个代码工作正常,返回了我[1,2,3]。我知道,调用__new__函数后,我们将第一个参数作为希望接收的类型进行传递。我们说的是New函数,对吗?除了绑定类型,我不知道其他语言返回类型?
我也做了另一件事:
1 2 3 4 5 6 7 8 9 10
| >>> issubclass(MyClass,list)
False
>>> issubclass(MyClass,tuple)
True
>>> isinstance(a,MyClass)
False
>>> isinstance(a,tuple)
False
>>> isinstance(a,list)
True |
我没有做更多的实验,因为更远的地方不亮,我决定停在那里,然后决定问StackOverflow。
我读到的SO帖子:
创建python对象
python使用了uuu new_uuuu和uuu init_uuuuu?
how I should structure the class using __init__ and __new__ as they are different and both accepts arbitrary arguments besides default first argument.
你很少会担心__new__。通常,您只需要定义__init__并让默认的__new__将构造函数参数传递给它。
self keyword is in terms of name can be changed to something else? But I am wondering cls is in terms of name is subject to change to something else as it is just a parameter name?
两者都只是参数名,在语言中没有特殊意义。但是他们的使用在Python社区中是一个非常强的约定;在这些上下文中,大多数pythonistas将永远不会更改名称self和cls,并且在其他人更改时会混淆。
注意,使用def __new__(tuple)会在构造函数函数内部重新绑定名称tuple。当实际实现__new__时,您将希望这样做
1 2 3
| def __new__(cls, *args, **kwargs):
# do allocation to get an object, say, obj
return obj |
Albeit I said I want to return tuple, this code works fine and returned me [1,2,3].
MyClass()将具有__new__返回的值。在python中没有隐式类型检查;程序员有责任返回正确的类型("我们都是同意的成年人")。能够返回与请求的类型不同的类型对于实现工厂很有用:您可以返回请求类型的子类。
这也解释了你所观察到的issubclass/isinstance行为:子类关系源于你使用class MyClass(tuple),isinstance反映你从__new__返回"错误"类型。
作为参考,请查看python语言参考中对__new__的要求。
编辑:好的,这里有一个使用__new__的可能有用的例子。Eel类跟踪进程中有多少鳗鱼存活,如果超过某个最大值,则拒绝分配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Eel(object):
MAX_EELS = 20
n_eels = 0
def __new__(cls, *args, **kwargs):
if cls.n_eels == cls.MAX_EELS:
raise HovercraftFull()
obj = super(Eel, cls).__new__(cls)
cls.n_eels += 1
return obj
def __init__(self, voltage):
self.voltage = voltage
def __del__(self):
type(self).n_eels -= 1
def electric(self):
"""Is this an electric eel?"""
return self.voltage > 0 |
记住,有更好的方法来完成这个行为。
- 但是,您能给我一个示例代码,其中包含__new__和__init__,并且传递了任意参数。
- @勇敢的院子:贴了一个玩具的例子。
- @据我所知,如果我们想使用__new__和__init__,我们应该从object继承吗?或者我们只是从object继承它来使用默认的__new__?
- @勇敢场:从以东王(2)继承,得到以东王(0)。事实上,最好总是从object继承,除非您正在为非常旧的Python版本编写向后兼容的代码。在python 3中,旧样式的类已不存在,来自object的继承在类定义中是隐式的。
- @拉斯曼现在说得有道理):从埃多克斯(EDOCX1)(2)继承的遗产隐含在3.0中。因为我更多地使用静态语言,所以从object继承总是含蓄的:)
- @ BravyAd:Python旧风格类更像C++中的,也没有全局根类。我认为,最早的Python版本根本没有类,为了将所有的Python类型统一到一个层次结构中,引入了新样式的类。
- @《勇敢的院子》:更正一下,如果我必须相信维基百科,那么Python就有了它最早版本的类。很抱歉。
- 您不应该从__new__中调用__init__,如果您返回一个cls类型的实例,python将自行调用。
- 我还要补充一点,能够从__new__中返回某些东西的看似奇怪的行为(至少在理论上)是有用的。我可以想象类似于BalancedBinaryTree类的东西,可以从列表中初始化。它有LeafNode和EmptyTree的子类,如果输入列表有0或1个元素,__new__返回其中一个。从本质上讲,它意味着您可以实现工厂而不需要看起来,因此您可以更改类是使用工厂还是直接实例化而不必破坏接口。
- @本:很好,补充说。
- 我觉得有个bug,应该是type(self).n_eels -= 1,不是self.n_eels -= 1。
- @你说得对,谢谢!
- 我在给你举鳗鱼的例子之前给了你+1。希望我能再给埃多克斯一个(23)
- 我不知道args和kwargs是如何从new过渡到init的。