10 python魔法、静态、类方法,特性和迭代器

1、魔法方法

  • 在Python中,有些名称的开头和结尾都是两个下划线。这些名称特殊意义,很大一部分都是魔法(特殊)方法的名称,因此绝不要在程序中创建这样的名称。
  • 魔法(特殊)方法将在特定情况下(具体是哪种情况取决于方法的名称)被Python调用,而几乎不需要直接调用。

旧式类和新式类

  • 如果你使用的不是Python3,即便你使用的是较新的Python2版本,有些功能(如特性和函数super)也不适用于旧式类。要让python2中的类是新式的(python2默认使用的是旧式类),要么在模块开头包含赋值语句__metaclass__ = type,要么直接或间接地继承内置类object或其他新式类。
  • 在Python3中没有旧式类,因此无需显式地继承object或将__metaclass__设置为type。所有的类都将隐式地继承object。如果没有指定超类,将直接继承它,否则将间接地继承它。
  • 新创建的类,必须是新式类。在Python2中非必要不要创建旧式类。

1.1、魔术方法__init__(构造函数)

  • 构造函数在创建对象时自动被调用
  • 构造函数用于初始化新建对象(实例)的状态。
def __init__(self, [参数1, 参数2...])
    语句

1、无参构造函数

class FooBar:
    def __init__(self):
        self.somevar = 'hi'
        print(self.somevar, '我在__init__中')

hh = FooBar()     #在创建对象的时候自动调用构造函数

<<<
hi 我在__init__中

2、有参构造函数

class FooBar:
    def __init__(self, semevar, name):
        self.somevar = semevar
        self.hh = name
        print('{} {},我在__init__中'.format(self.somevar, self.hh))

hh = FooBar('hello', 'hengha')    #构造函数带参时,创建对象的时候就要传参

<<<
hello hengha,我在__init__中

1.2、重写构造函数

  • 要在子类中添加功能,一种方式是添加方法,另一种方式是重写超类的某些方法,以定制继承而来的行为。
  • 对于大多数子类来说,除超类的初始化代码外,还需要有自己的初始化代码。
  • 虽然所有方法的重写机制都相同,但与重写普通方法相比,重写构造函数时更有可能遇到一个特别的问题:重写构造函数时,必须调用父类的构造函数,否则可能无法正确地初始化对象。
  • 重写构造函数的两种方法:调用未关联的超类构造函数,以及使用函数super。

1、不调用父类的构造函数,可能有异常

  • 异常清楚地指出了问题出在什么地方:SongBird没有属性hungry。为何会这样呢?因为在SongBird中重写了构造函数,但新的构造函数没有包含任何初始化属性hungry的代码。
class Bird:                #声明父类
    def __init__(self, food):
        self.hungry = True
        self.food = food
    def eat(self):
        if self.hungry:
            print('Aaaah ...小鸟在吃{}'.format(self.food))
            self.hungry = False
        else:
            print('No, thanks!')

class SongBird(Bird):     #声明子类
    def __init__(self, sound):
        self.sound = sound
    def sing(self):
        print(self.sound)

b = Bird('小米')
b.eat()      #结果是:Aaaah ...小鸟在吃小米
b.eat()      #结果是:No, thanks!

sb=SongBird('啾啾...')
sb.sing()    #结果是:啾啾...
sb.eat()        #调用sb.eat

<<<             #调用sb.eat的结果  #子类继承了父类的方法,但由于子类重写构造函数,因此没有调用父类的构造函数,也就没有初始化到hungry变量
Traceback (most recent call last):
  File "D:/test/python/day/test.py", line 24, in <module>
    sb.eat()
  File "D:/test/python/day/test.py", line 6, in eat
    if self.hungry:
AttributeError: 'SongBird' object has no attribute 'hungry'

2、调用未关联的超类构造函数

  • 实例调用方法时,方法的参数self将自动关联到实例,称为关联的方法
  • 如果通过类调用方法,就没有实例与其相关联。在这种情况下,可以随便设置参数self。这样的方法称为未关联的方法
###声明父类
class 父类名:
    def __init__(self, 参数名a1, 参数名a2):
        self.val_a1 = 参数名a1
        self.val_a2 = 参数名a2

声明子类
class 子类名(父类名):
    def __init__(self, 参数名a1, 参数名a2, 参数名b1, 参数名b2):
        子类名.__init__(self, 参数名a1, 参数名a2)    #注意,这里带self了
        self.val_b1 = 参数名a1
        self.val_b2 = 参数名a2

###创建对象
对象名1 = 父类名(参数名a1, 参数名a2)

对象名2 = 子类名(参数名a1, 参数名a2, 参数名b1, 参数名b2)

示例:

  • 在SongBird类中,只添加了一行代码Bird.__init__(self, food, sound)。
  • 通过将这个未关联方法的self参数设置为当前实例,将使用超类的构造函数来初始化SongBird对象。这意味着将设置其属性hungry。
class Bird:
    def __init__(self, food):
        self.hungry = True
        self.food = food
    def eat(self):
        if self.hungry:
            print('Aaaah ...小鸟在吃{}'.format(self.food))
            self.hungry = False
        else:
            print('No, thanks!')

class SongBird(Bird):
    def __init__(self, food, sound):
        Bird.__init__(self, food)    #父类名.__init(self,[参数1,参数2,...]),参数是父类的构造函数的参数
        self.sound = sound
    def sing(self):
        print(self.sound)

b = Bird('小米')
b.eat()      #结果是:Aaaah ...小鸟在吃小米
b.eat()      #结果是:No, thanks!

sb = SongBird('高粱', '啾啾...')
sb.sing()    #结果是:啾啾...
sb.eat()     #结果是:Aaaah ...小鸟在吃高粱
sb.eat()     #No, thanks!

3、使用super函数

  • super函数只能用在新式类中
  • 调用super函数时,将当前类当前实例作为参数。其返回的对象调用的方法是超类(而不是当前类)的方法
  • 在Python3中调用super函数时,可不提供任何参数(通常也应该这样做)。
  • 即便有多个超类,也只需调用super函数一次(条件是所有超类的构造函数也使用super函数)。
    对于使用旧式类时处理起来很棘手的问题(如两个超类从同一个类派生而来),在使用新式类和super函数时将自动得到处理。
    无需知道super函数的内部工作原理,但必须要知道的是,使用super函数比调用超类的未关联构造函数(或其他方法)要好得多。

  • super函数返回的到底是什么呢?通常,你无需关心这个问题,只管假定它返回你所需的超类即可。实际上,它返回的是一个super对象,这个对象将负责为你执行方法解析。当你访问它的属性时,它将在所有的超类(以及超类的超类,等等)中查找,直到找到指定的属性或引发AttributeError异常。
###声明父类
class 父类名:
    def __init__(self, 参数名a1, 参数名a2):
        self.val_a1 = 参数名a1
        self.val_a2 = 参数名a2

###声明子类
class 子类名(父类名):
    def __init__(self, 参数名a1, 参数名a2, 参数名b1, 参数名b2):
        super.__init__(参数名a1, 参数名a2)   #注意,这里没有self
        self.val_b1 = 参数名a1
        self.val_b2 = 参数名a2

###创建对象
对象名1 = 父类名(参数名a1, 参数名a2)

对象名2 = 子类名(参数名a1, 参数名a2, 参数名b1, 参数名b2)

示例:

  • 在SongBird的构造函数中,可不使用Bird,而是使用super(SongBird,self)调用父类的__init__方法。在python3中super函数可以不带参数。
class Bird:
    def __init__(self, food):
        self.hungry = True
        self.food = food
    def eat(self):
        if self.hungry:
            print('Aaaah ...小鸟在吃{}'.format(self.food))
            self.hungry = False
        else:
            print('No, thanks!')

class SongBird(Bird):
    def __init__(self, food, sound):
        super().__init__(food)    #子类重写构造函数,必须调用父类的构造函数
        self.sound = sound
    def sing(self):
        print(self.sound)

b = Bird('小米')
b.eat()      #结果是:Aaaah ...小鸟在吃小米
b.eat()      #结果是:No, thanks!

sb = SongBird('高粱', '啾啾...')
sb.sing()    #结果是:啾啾...
sb.eat()     #结果是:Aaaah ...小鸟在吃高粱
sb.eat()     #No, thanks!

1.3、魔术方法__str__

  • __str__()方法在使用print()和str()函数打印对象名时自动被调用。
  • 必须在__str__方法中添加return, return后面内容就是打印对象名时的提示信息,且必须时字符串。
  • 单纯打印对象名称,出来的是一个地址。地址对于开发者来书没有太大意义。如果想在打印对象名的时候能够给开发者更多一些信息量,就可以使用__str__方法。
class FooBar:
    def __init__(self):
        self.somevar = 'hi'
    def __str__(self):
        return '{},我在__str__中'.format(self.somevar)

hh = FooBar()

print(hh)        #结果是:hi,我在__str__中

hh1 = str(hh)
print(hh1)       #结果是:hi,我在__str__中

hh2 = repr(hh)
print(hh2)       #结果是:<__main__.FooBar object at 0x0000027C7AB48CF8>

1.4、其他魔术方法

1、魔术方法__repr__

  • __repr__()方法在使用repr()函数打印对象名时自动被调用。
  • 当__repr__和__str__同时存在时,使用print()函数打印对象名,__str__方法生效。
class FooBar:
    def __init__(self):
        self.somevar = 'hi'
    def __repr__(self):
        return 'hhh'
    def __str__(self):
        return '{},我在__str__中'.format(self.somevar)

hh = FooBar()
print(hh)

hh1 = repr(hh)
print(hh1)

2、魔术方法__new__

  • __new__方法在创建对象的时自动被调用。申请内存,开辟内存空间,并返回内存地址给类对象__main__(),然后加载__init__()进行初始化对象。
  • 实例化对象是Object类底层实现的,其他类继承了Object的__new__才能够实现实例化对象。
  • 创建对象时先触发__new__后才会触发__init__。

3、魔术方法__del__(析构方法)

  • 当一块空间(对象)没有任何引用时自动执行__del__,回收内存。
###对象赋值
p = Person()
p1 = p

###删除地址的引用
del p             #删除p1对地址的引用

###查看对地址的引用次数
import sys
sys. getcefcount (p)

2、特性

  • 在Python中,实际上有两种创建特性的机制

2.1、使用property函数创建特性

  • 通过存取方法定义的属性通常称为特性(property)。
  • 特性(property)以前是通过魔法方法处理的,现在通过property函数处理。
  • 使用property函数创建特性。property其实并不是函数,而是一个类。
  • property函数只能用于新式类。
  • 新式类应使用特性而不是存取方法。
  • 调用property函数时,可以不指定参数、指定一个参数、指定两个参数、指定三个参数或指定四个参数。
    如果没有指定任何参数,创建的特性将既不可读也不可写。
    如果只指定一个参数(获取方法),创建的特性将是只读的。
    如果只指定两个参数(获取方法,设置方法),创建的特性可读可写。
    第三个参数是可选的,指定用于删除属性的方法(这个方法不接受任何参数)。
    第四个参数也是可选的,指定一个文档字符串。
    这些参数分别名为fget、fset、fdel和doc。
    如果你要创建一个只可写且带文档字符串的特性,可使用它们作为关键字参数来实现。

1、不使用property函数

class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def set_size(self, size):     #设置属性的方法
        self.width, self.height = size
    def get_size(self):           #获取属性的方法
        return self.width, self.height

r = Rectangle()

r.width = 10
r.height = 5
print(r.get_size())    #结果是:(10, 5)
r.set_size((100, 50))
print(r.width)         #结果是:100

2、使用property函数

  • 在这个新版的Rectangle中,通过调用property函数并将存取方法作为参数(获取方法在前,设置方法在后)创建了一个特性,然后将名称size关联到这个特性。这样,你就能以同样的方式对待width、height和size,而无需关心它们是如何实现的。
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def set_size(self, size):
        self.width, self.height = size
    def get_size(self):
        return self.width, self.height
    size = property(get_size, set_size)    #size是特性,通过存取方法定义的属性

r = Rectangle()

r.width = 10
r.height = 5
print(r.size)          #结果是:(10, 5)
r.size = (100, 50)
print(r.width)         #结果是:100

3、@property装饰器的使用

  • 先使用“@property”装饰“get方法”,再用“@get方法名.setter”装饰“set方法”。
  • “get方法”和“set方法”的名字是一样的
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0

    @property
    def size(self):               #get方法
        return self.width, self.height
    @size.setter
    def size(self, size):         #set方法
        self.width, self.height = size

r = Rectangle()
r.width = 10
r.height = 5
print(r.size)          #结果是:(10, 5)
r.size = (100, 50)
print(r.width)         #结果是:100

2.2、__getattr__、__setattr__等魔法方法

  • 要在属性被访问时执行一段代码,必须使用下面的魔法方法(在旧式类中,只有后面三个)。。
    • __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)。
    • __getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用。
    • __setattr__(self, name, value):试图给属性赋值时自动调用。
    • __delattr__(self, name):试图删除属性时自动调用。
  • 编写__setattr__和__getattribute__方法时需要避开无限循环陷阱。
  • __getattr__和__setattr__方法用途之一是在旧式类中实现特性(在旧式类中,函数property的行为可能不符合预期)。

示例:使用__getattr__、__setattr__方法实现特性

class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.height = value
        else:
            self.__dict__[name] = value
    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.height
        else:
            raise AttributeError()
  • 对于这个代码示例,需要注意如下两点。
    • 即便涉及的属性不是size,也将调用方法__setattr__。因此这个方法必须考虑如下两种情形:如果涉及的属性为size,就执行与以前一样的操作;否则就使用魔法属性__dict__。__dict__属性是一个字典,其中包含所有的实例属性。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用__setattr__,进而导致无限循环。
    • 仅当没有找到指定的属性时,才会调用方法__getattr__。这意味着如果指定的名称不是size,这个方法将引发AttributeError异常。这在要让类能够正确地支持hasattr和getattr等内置函数时很重要。如果指定的名称为size,就使用前一个实现中的表达式。

3、静态方法和类方法

  • 静态方法和类方法分别包装在staticmethod和classmethod类的对象中。
class MyClass:
    def smeth():
        print('This is a static method')
    smeth = staticmethod(smeth)   #静态方法

    def cmeth(cls):
        print('This is a class method of', cls)
    cmeth = classmethod(cmeth)    #类方法
  • 像这样手工包装和替换方法有点繁琐,可以使用装饰器。
class MyClass:
    @staticmethod                 #静态方法
    def smeth():
        print('This is a static method')

    @classmethod                  #类方法
    def cmeth(cls):
        print('This is a class method of', cls)

MyClass.smeth()
MyClass.cmeth()

1、静态方法的特点和作用

  • 静态方法的定义中没有参数self。
  • 静态方法直接通过类调用。
  • 静态方法只能访问类属性和类方法。
  • 静态方法的作用,静态方法只能访问类属性和类方法,因此可以在创建对象之前完成一些动作(功能)

2、类方法的特点和作用

  • 类方法中参数不是一个对象,而是类,通常被命名为cls。
  • 类方法可以通过类调用,也可以通过对象直接调用,但参数cls将自动关联到类。
  • 类方法中只可以使用类属性。
  • 类方法中不可以使用普通方法
  • 类方法的作用,类方法只能访问类属性和类方法,因此可以在创建对象之前完成一些动作(功能)。

3、静态方法和类方法的比较

  • 不同之处:
    • 装饰器不同
    • 类方法有参数,静态方法没有参数
  • 相同之处:
    • 都只能访问类的属性和方法,无法访问对象的属性和方法。
    • 都可以通过类名调用访问
    • 都可以在创建对象之前使用,因为是不依赖于对象

4、普通方法与两者的区别

  • 普通方法没有装饰器
  • 普通方法永远都要依赖对象,因为每个普通方法都有self参数
  • 只有创建了对象才可以调用普通方法,否则无法调用。

4、迭代器

  • 迭代(iterate)重复多次,就像循环那样。
  • 可迭代对象:可以使用for循环进行遍历的对象。
  • 迭代器:可以被next()函数调用,并返回下一个值的对象。
    • 魔法方法__iter__是迭代器协议的基础。
    • __iter__方法返回一个迭代器(迭代器自己),它是包含__next__方法的对象,而调用这个方法时可不提供任何参数。当调用__next__方法时,迭代器应返回其下一个值。如果迭代器没有可供返回的值,应引发StopIteration异常。还可以使用内置的便利函数next,next(it)与it.__next__()等效
    • 在Python3以前的迭代器协议中,要求迭代器对象包含next方法而不是__next__。
  • 可迭代对象:
    • 迭代器(包含生成器)
    • 序列(包含字符串、列表、元组)
    • 字典
  • 结论:
    1. 可迭代对象包含迭代器。(迭代器是可迭代的,但可迭代的不一定是迭代器。)
    2. 实现了方法__iter__的对象是可迭代的,而实现了方法__next__的对象是迭代器。
    3. 定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和__next__方法。

1、判断一个对象是否是可迭代的

from collections import Iterable

list1 = [1, 2, 3, 4, 5]
str1 = 'hengha'
int1 = 100
print(isinstance(list1, Iterable))    #结果是:True
print(isinstance(str1, Iterable))     #结果是:True
print(isinstance(int1, Iterable))     #结果是:False

2、使用类实现迭代器

  • 注意到这个迭代器实现了方法__iter__,而这个方法返回迭代器本身。在很多情况下,都在另一个对象中实现返回迭代器的方法__iter__,并在for循环中使用这个对象。
  • 推荐在迭代器中也实现方法__iter__(并像刚才那样让它返回self),这样迭代器就可直接用于for循环中。
class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a
    def __iter__(self):
        return self

fibs = Fibs()

print(fibs.__next__())    #结果是:1
print(fibs.__next__())    #结果是:1
print(next(fibs))         #结果是:2
print(next(fibs))         #结果是:3

3、将可迭代对象转换为迭代器

  • 内置函数iter可以将可迭代对象转换为迭代器
list1 = [1, 2, 3, 4, 5]
iter1 = iter(list1)

print(next(iter1))        #结果是:1
print(next(iter1))        #结果是:2

4、从迭代器创建序列

  • 使用构造函数list显式地将迭代器转换为列表。
class TestIterator:
    value = 0
    def __next__(self):
        self.value += 1
        if self.value > 10:
            raise StopIteration
        return self.value
    def __iter__(self):
        return self

ti = TestIterator()
print(list(ti))           #结果是:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5、生成器

  • 在Python中,包含yield语句的函数都被称为生成器(generator)。
  • 生成器是一种使用普通函数语法定义的迭代器
  • 生成器由两个单独的部分组成:生成器的函数和生成器的迭代器。生成器的函数是由def语句定义的,其中包含yield。生成器的迭代器是这个函数返回的结果。
  • 生成器被调用时不会执行函数体内的代码,而是返回一个迭代器。每次请求值时,都将执行生成器的代码,直到遇到yield或return。yield意味着应生成一个值,而return意味着生成器应停止执行(即不再生成值;仅当在生成器调用return时,才能不提供任何参数)。
  • 生成器的行为与普通函数不同。生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。
  • 内置函数next()和__next__方法,可以获取生成器的下一个值。

1、生成器推导(也叫生成器表达式)

  • 生成器推导工作原理与列表推导相同,但使用的是圆括号,且不是创建一个列表(即不立即执行循环),而是返回一个生成器。
###列表推导
list1 = [x * x for x in range(10)]
print(list1)               #结果是:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(type(list1))         #结果是:<class 'list'>

###生成器推到
list1 = (x * x for x in range(10))
print(list1)               #结果是:<generator object <genexpr> at 0x000001C6AB88F570>
print(type(list1))         #结果是:<class 'generator'>
print(list1.__next__())    #结果是:0
print(list1.__next__())    #结果是:1
print(next(list1))         #结果是:4
print(next(list1))         #结果是:9

2、使用函数声明生成器

###函数
def func():
    n = 0
    while True:
        n += 1
        print(n)

func()                 #结果是:死循环    

###生成器
def func():
    n = 0
    while True:
        n += 1
        yield n     #yield的作用相当于,return n + 暂停

gen = func()        #返回一个迭代器
print(gen)             #结果是:<generator object func at 0x000001F25C02F570>
print(gen.__next__())  #结果是:1
print(next(gen))       #结果是:2

3、在生成器中的return

  • return可以在发生StopIteration异常时提供提示信息。
###斐波那契生成器
def fib(length):
    a, b = 0, 1
    n = 0
    while n < length:
        yield b        #yield的作用相当于,return b + 暂停
        a, b = b, a + b
        n += 1
    return '没有更多的元素了'

gen =fib(3)
print(gen.__next__())    #结果是:1
print(gen.__next__())    #结果是:1
print(gen.__next__())    #结果是:2
print(gen.__next__())  #异常,StopIteration: 没有更多的元素了

4、生成器的send方法

  • send()方法可以在调用生成器时向其传值。
  • 仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义。要在此之前向生成器提供信息,可使用生成器的函数的参数。
  • 如果一定要在生成器刚启动时对其调用方法send,可向它传递参数None。
def gen():
    i = 1
    while i < 10:
        temp = yield i
        print('temp:', temp)
        for x in range(temp):
            print('---------------------->', x)
        print('***********************')
        i += 1
    return '没有更多数据了'

gen = gen()

gen.send(None)
gen.send(2)
gen.send(4)

5、生成器的使用

  • 生成器可用于协程,模拟多进程。(进程 > 线程 > 协程)
def task1(n):
    for i in range(1, n):
        print('正在搬第{}块砖!'.format(i))
        yield None

def task2(n):
    for i in range(1, n):
        print('正在听第{}首歌!'.format(i))
        yield None

t1 = task1(4)
t2 = task2(4)
while True:
    try:
        t1.__next__()
        t2.__next__()
    except:
        break

6、反射

  • python中的反射功能是由四个内置函数提供:hasattr、getattr、setattr、delattr。这四个函数分别用于:检查对象中是否含有某成员、获取对象中的成员、设置对象中的成员、删除对象中成员。
  • 对象、类、模块都可以使用反射。
class Foo(object):
    def __init__(self):
        self.name = 'hengha'
    def func(self):
        return 'func'
obj = Foo()
 
#检查是否含有成员
hasattr(obj, 'name')
hasattr(obj, 'func')
#获取成员
getattr(obj, 'name')
getattr(obj, 'func')
#设置成员
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
#删除成员
delattr(obj, 'name')
delattr(obj, 'func')
  • 不使用obj.name获取其值

示例1:

class Foo(object):
    def __init__(self):
        self.name = 'hengha'
    def func(self):
        return 'func'
obj = Foo()

print obj.__dict__['name']

示例2:

class Foo(object):
    def __init__(self):
        self.name = 'hengha'
    def func(self):
        return 'func'
obj = Foo()

print getattr(obj, 'name')

7、单例模式

  • 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

1、使用__new__创建单例(推荐使用)

class Singleton:
    __instence = None    #使用私有变量来判断该类是否已有实例,并存储实例的内存地址
    def __new__(cls):
        if cls.__instence is None:
            cls.__instence = object.__new__(cls)
            return cls.__instence
        else:
            return cls.__instence

s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)

2、使用类方法来创建单例

class Singleton:
    __instence = None
    @classmethod
    def get_instance(cls):
        if cls.__instence:
            return cls.__instence
        else:
            cls.__instence = Singleton()
            return cls.__instence

s1 = Singleton.get_instance()
s2 = Singleton.get_instance()
print(s1)
print(s2)

 

posted @ 2021-02-24 11:21  麦恒  阅读(122)  评论(0编辑  收藏  举报