草庐IT

python - 当类实例由构造函数或 __new__ 创建时,确保 __init__ 只被调用一次

coder 2023-08-19 原文

我试图理解当创建过程可以通过构造函数或通过 __new__ 方法时,应该如何创建 Python 类的新实例。特别是,我注意到在使用构造函数时,__init__ 方法将在 __new__ 之后自动调用,而当直接调用 __new__ 时, __init__ 类不会被自动调用。我可以通过在 __new__ 中嵌入对 __init__ 的调用,在显式调用 __new__ 时强制调用 __init__,但是然后 __init__ 将在通过构造函数创建类时最终被调用两次。

例如,考虑下面的玩具类,它存储一个内部属性,即一个名为 datalist 对象:将此视为向量类。

class MyClass(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        obj.__init__(*args, **kwargs)
        return obj

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

    def __getitem__(self, index):
        return self.__new__(type(self), self.data[index])

    def __repr__(self):
        return repr(self.data)

可以使用构造函数(实际上不确定这是否是 Python 中的正确术语)来创建类的新实例,例如

x = MyClass(range(10))

或通过切片,您可以看到在 __getitem__ 方法中调用了对 __new__ 的调用。

x2 = x[0:2]

在第一个实例中,__init__ 将被调用两次(均通过 __new__ 中的显式调用,然后自动再次调用),在第二个实例中调用一次。显然我只希望 __init__ 在任何情况下都被调用一次。有没有在 Python 中执行此操作的标准方法?

请注意,在我的示例中,我可以去掉 __new__ 方法并将 __getitem__ 重新定义为

def __getitem__(self, index):
    return MyClass(self.data[index])

但是如果我以后想从 MyClass 继承,这会导致问题,因为如果我像 child_instance[0:2] 那样调用,我会返回MyClass 的实例,而不是子类。

最佳答案

首先,关于 __new____init__ 的一些基本事实:

  • __new__ 是一个构造函数
  • __new__ 通常返回 cls 的实例,它的第一个参数。
  • 通过__new__返回cls的实例,__new__ causes Python to call __init__ .
  • __init__ 是一个初始化程序。它修改实例 (self) 由 __new__ 返回。它不需要返回 self

MyClass 定义时:

def __new__(cls, *args, **kwargs):
    obj = object.__new__(cls, *args, **kwargs)
    obj.__init__(*args, **kwargs)
    return obj

MyClass.__init__ 被调用了两次。一次是显式调用 obj.__init__,第二次是因为 __new__ 返回了 objcls 的一个实例。 (由于 object.__new__ 的第一个参数是 cls,返回的实例是 MyClass 的实例,所以 obj.__init__ 调用 MyClass.__init__,而不是 object.__init__。)


Python 2.2.3 release notes有一个有趣的评论,它阐明了何时使用 __new__ 以及何时使用 __init__:

The __new__ method is called with the class as its first argument; its responsibility is to return a new instance of that class.

Compare this to __init__:__init__ is called with an instance as its first argument, and it doesn't return anything; its responsibility is to initialize the instance.

All this is done so that immutable types can preserve their immutability while allowing subclassing.

The immutable types (int, long, float, complex, str, unicode, and tuple) have a dummy __init__, while the mutable types (dict, list, file, and also super, classmethod, staticmethod, and property) have a dummy __new__.

因此,使用__new__ 定义不可变类型,使用__init__ 定义可变类型。虽然可以同时定义两者,但您不需要这样做。


因此,由于 MyClass 是可变的,您应该只定义 __init__:

class MyClass(object):
    def __init__(self, data):
        self.data = data

    def __getitem__(self, index):
        return type(self)(self.data[index])

    def __repr__(self):
        return repr(self.data)

x = MyClass(range(10))
x2 = x[0:2]

关于python - 当类实例由构造函数或 __new__ 创建时,确保 __init__ 只被调用一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8633959/

有关python - 当类实例由构造函数或 __new__ 创建时,确保 __init__ 只被调用一次的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  5. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  6. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  7. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  8. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  9. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐