•   欢迎来到21NN网.
  •   请记住本站网址www.21nn.cn

Python中new类要领和init 实例要领以及单例形式的引见(附示例)【Python教程】,python

摘要: 本篇文章给人人带来的内容是关于Python中new类要领和init实例要领以及单例情势的引见(附示例),有肯定的参考价值,有须要的朋侪能够参考一下,愿望对你有所协助。“Python中的类都...
本篇文章给人人带来的内容是关于Python中new类要领和init 实例要领以及单例情势的引见(附示例),有肯定的参考价值,有须要的朋侪能够参考一下,愿望对你有所协助。

“Python 中的类都是单例情势?” 一天,一同事问我如许一个题目。这是一个新鲜的题目,能够你也这么以为。这里先不做诠释,我们先来看看 __new__ 和 __init__ 要领。

new 与 init

__new__ 要领属于新式类,即属于 object 类。它是一个静态要领,然则其第一个参数必需是一个类(cls),这有点像一个 classmethod,实在将其看成是一个类要领也能够。该特别要领被挪用时,会建立类(cls)的一个新实例并返回,实例被建立后诠释器会将该实例以及别的的参数传递给该实例的初始化函数 __init__,以对实例举行初始化。

所以,__new__ 要领是一个类要领,用于建立一个实例,而 __init__ 要领是一个实例要领,用于初始化一个实例。

__new__ 要领在实例化一个类时被挪用,重写该要领应当像以下的情势:

class A(object):

    def __new__(cls, *args, **kwargs)
        return super(A, cls).__new__(cls, *args, **kwargs)

假如 __new__ 要领不返回 cls 的一个实例,那末新的实例的 __init__ 要领不会被挪用。须要注重的是,在 Python 3.3 今后,new 要领不再吸收分外的参数,不然会有异常 TypeError: object() takes no parameters。

__init__ 要领在实例被建立今后被挪用,该要领仅仅是对 __new__ 要领建立的实例举行一些初始化操纵。注重,假如 new 要领返回实例,则 init 要领老是会被挪用(这一点在用 new 要领完成单例时要特别注重)

能够来做一下考证:

class Foo(object):

    def __new__(cls, m, n):
        print "__new__ is called"
        return super(Foo, cls).__new__(cls, m, n)

    def __init__(self, m, n):
        print "__init__ is called"
        self.m = m
        self.n = n

    def __repr__(self):
        return "Foo(m={self.m}, n={self.n})".format(self=self)

    def __str__(self):
        return self.__repr__()


if __name__ == "__main__":
    f = Foo(1, 2)
    print f

输出结果:

__new__ is called
__init__ is called
Foo(m=1, n=2)

因而能够得出结论:

1、 __new__ 属于类级别的要领,纵然没有被 classmethod 装潢,其决议生成实例的历程。

2、 __init__ 属于实例级别的要领,决议实例初始化的历程,比方增加属性,对初始化参数举行推断,转换等。

须要注重的是,在重写 __new__ 要领与 __init__ 要领的参数应当保持一致,不然会有 TypeError 发作。假如直接挪用 object.__new__() 则在 Python 3.3 及今后的版本中不再支撑传入参数,这一点参考自:https://stackoverflow.com/questions/34777773/typeerror...

__init__ 要领,在定义一个 class 的时刻平常都邑涉及到,也是比较经常运用。而 __new__ 要领则很少会用到,那末它到底有什么用处呢?

new 要领作用

__new__ 要领比较经常运用的作用大概是:

1、 继续内建不可变范例时(比方int, str, tuple),供应自定义实例化历程。因为假如在 __init__ 要领中做都写操纵能够会无效。比方:

class CustomInt(int):

    def __init__(self, v):
        super(CustomInt, self).__init__(self, abs(v))

print CustomInt(-1)

# 输出:-1

这能够没有到达希冀的结果。但能够经由过程重写 __new__ 要领来完成:

class CustomInt(int):

    def __new__(cls, v):
        return super(CustomInt, cls).__new__(cls, abs(v))

print CustomInt(-1)

# 输出:1
  • 2、 完成自定义的元类(metaclass)。元类就是用来定义怎样建立类,它的观点能够轻微庞杂些,这里不做细致议论。

  • 3、 完成单例。因为类发生实例的历程是经由过程 __new__ 要领来掌握的,因而重写该要领来单例情势是异常轻易的:

class Singleton(object):

    def __new__(cls):
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

assert Singleton() is Singleton()  # 断言胜利

所谓单例情势就是每次初始化都返回同一个实例,所以两次初始化获得的对象的内存地址应当是一样的:

print Singleton(), Singleton()

结果应当是不言而喻的:

<__main__.Singleton object at 0x10d698650> <__main__.Singleton object at 0x10d698650>

装潢器完成单例

说到单例情势,除了用 __new__ 要领完成外,另有一些其他的体式格局,如装潢器、元类等。差别体式格局的完成有差别的作用,元类属于 Python 中越发高等的特征,本文不做议论,我们来看一下用装潢器完成单例的要领。

装潢器(decorator)能够动态地修改一个类或函数的功用,即类也能够被装潢器装潢。因而能够运用装潢器来装潢某个类,使其被初始化时只生成一个实例:

from functools import wraps

def singleton(cls):
    instances = {}
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

@singleton
class MyClass(object):

    def __init__(self):
        pass

须要注重的是,运用装潢器完成单例时,类已变成了一个函数,而不再是类。 如上用上例中 MyClass 初始化实例时,实际上挪用的是被装潢后返回的 getinstance 函数。

__new__ 完成单例和用装潢完成单例的区别是,前者前者都是会挪用 __init__ 要领,这就意味着每次初始化时用差别的参数,虽然返回的实例时同一个,然则实例的属性却被从新设置了;而后者则老是返回第一次初始化建立的示例和设置的属性,纵然背面传入了差别的参数。

新鲜征象

接着,我们再来看一个 “新鲜” 的征象:

>>> class A(object):
...     pass
...
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>

是不是是以为有些难以置信,print 语句后两次建立的对象应当是不一样的,而他们却莫名巧妙的一样。这就是我议论本文内容的缘由。

一次同事问我,Python 中的类都是单例情势?我当时一脸懵逼,听了他的形貌,我本身也试了下,果真存在如上所示的“新鲜”征象。因而我就去相识了 Python 单例情势的完成,在相识到 __new__ 的完成体式格局时,就想对 __new____init__ 有一个越发深切的相识。因而就有了本文所议论的内容。

然后,我想着用 is 来推断下他们是不是真的是同一个实例:

>>> A() is A()
False

我没有对 CPython 的源码举行过周全的浏览,所以对其许多内部的完成机制不是太相识。我猜 Python 诠释器在内部能够做了优化,像 print A(), A() 如许的语句,诠释器以为没有必要建立差别的对象,直接返回同一个实例的援用得了。是不是是以为诠释器有些自作聪明!而当 A() is A() 如许的表达式出现时,诠释器想,我不能再自作聪明,那样能够会误导他人。但是,在 print 语句那样的用法时,就已误导我了,我都差点最先疑心人生了!

从语法来看,人人应当晓得,我在测试时运用的 Python 2。我厥后也试了下 Python 3:

>>> class A():
...     pass
...
>>> print(A(), A())
<__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed79e8>
>>> print(A(), A())
<__console__.A object at 0x10fec0cc0> <__console__.A object at 0x10feda160>
>>> print(A(), A())
<__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed7940>
>>> A() is A()
False

我想,如许的结果才是不会让人疑心的。多是 Python 社区意识到了这个题目并在 Python3 中举行了修改。如许的修改是好的,不然关于像我同事那样首次运用 Python 的人来说是很疑心的。

个人以为,Python3 对过去的一些“毛病”的修改是好的。比方将 print 改成函数,供应了雄厚的参数来掌握输出的款式;对编码的调解等等。

以上就是Python中new类要领和init 实例要领以及单例情势的引见(附示例)的细致内容,更多请关注ki4网别的相干文章!

分享到:

发表评论

评论列表

还没有评论,快来说点什么吧~

公众号二维码

微信公众号