Python面向对象从入门到精通
面向对象中的类和实例
- 类 是众多具有相似属性实例的一种抽象,比如老师、学生这是两个类,类是指抽象出的模板。
- 实例 是类的一个个具体体现,比如我们学校有郑老师、张老师, 有小张同学、小余同学,实例是根据类创建出来的具体的对象。每个对象都拥有从类中继承的相同的方法,但各自的数据互相独立,互不影响。
定义类和实例
class Student(object):
def __init__(self, name, home='beijing'):
self.name = name
self.home = home
def __str__(self):
return self.name
s1 = Student('yuziyue')
s2 = Student('yuchaoshui')
print(s1)
print(s2)
首先定义了一个学生的类。__init__()
是初始化函数,初始化定义了name
、home
。__str__()
是类的特殊函数,打印该类的对象时会调用该函数。然后根据 Student
对象实例化两个对象 s1
、s2
,并打印出s1
、s2
初始化函数__init__
初始化函数的意思是在对象定义的时候,给该对象属性的值初始化。
class Student(object):
def __init__(self, name, home='beijing'):
self.name = name
self.home = home
def __str__(self):
return self.name
class Grad(Student):
def __init__(self, name, home='beijing', title='teacher'):
super().__init__(name, home)
self.title = title
def __str__(self):
return self.name
s = Student('zhanghong')
g = Grad('xiaowang')
print(s)
print(g)
print(g.title)
现在定义了两个类Student
、Grad
,Grad从Student继承而来,两个类分别都有自己的初始化函数__init__()
, 如果子类希望继承父类的属性,就必须使用 super()
函数调用父类的初始化函数,也可以像这样调用父类 super(Grad, self).__init__(name, home)
,注意:不推荐使用 类名.__init__()
这种方式调用初始化函数,后期可能会出现问题。
构造函数__new__
__new__
方法接受的参数和__init__
一样。 __init__
是在类实例创建之后调用,用作初始化对象的属性。而 __new__
方法正是创建这个类实例的方法。调用顺序是先调用__new__()
,然后调用__init__()
。
- 1.
__init__
通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。 - 2.
__new__
通常用于控制生成一个新实例的过程。它是类级别的方法。
class Person(object):
def __new__(cls, name, age):
print('__new__ called.')
return super(Person, cls).__new__(cls)
def __init__(self, name, age):
print('__init__ called.')
self.name = name
self.age = age
def __str__(self):
return 'instance: ' + self.name
if __name__ == '__main__':
yuchaoshui = Person('yuchaoshui', 24)
print(yuchaoshui)
print(yuchaoshui.name)
print(yuchaoshui.age)
# 结果如下:
__new__ called.
__init__ called.
instance: yuchaoshui
yuchaoshui
24
下面是改进测试版,把 __init__()
函数给去掉了同样可以初始化对象。
class Person(object):
def __new__(cls, name, age):
print('__new__ called.')
instance = super(Person, cls).__new__(cls)
instance.name = name
instance.age = age
return instance
def __str__(self):
return 'instance: ' + self.name
if __name__ == '__main__':
yuchaoshui = Person('yuchaoshui', 24)
print(yuchaoshui)
print(yuchaoshui.name)
print(yuchaoshui.age)
# 结果如下
__new__ called.
instance: yuchaoshui
yuchaoshui
24
类和对象的属性和方法
实例属性
实例属性就是与实例绑定的属性,可以用 实例名.属性名
的方式调用,它代表了一个实例的数据部分。
class Student(object):
def __init__(self, name, home='beijing'):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
print(s.name)
类属性
类属性是类的属性,当定义了一个类属性后,这个属性虽然归类所有,但类和实例都可以访问到,类属性是类和类所有实例的共有数据。当实例属性和类属性同名时,访问顺序是:实例属性名--》类属性名,如果都不存在则报错。
class Student(object):
school = 'beijing university'
def __init__(self, name, home='beijing'):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
print(s.name)
print(s.school)
print(Student.school)
实例方法
实例方法就是与实例绑定的函数,可以用 实例名.方法名
的方式调用,它的第一个参数表示实例本身,通常用 self
来表示,实例方法可以直接访问实例的数据做处理。通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
class Student(object):
def __init__(self, name, home='beijing'):
self.name = name
def __str__(self):
return self.name
def gotoclass(self):
act = "{} is going to class!".format(self.name)
print(act)
s = Student('yuziyue')
s.gotoclass()
类方法
类方法用于访问类属性,用@classmethod
装饰器来装饰,第一个参数是 cls
, cls
用于调用类属性,但不能访问实例属性!
使用场景1:由于python类中只能有一个初始化方法,不能按照不同的情况初始化类。使用类方法可以定义多个初始化函数的情况。
使用场景2:用于调用静态方法,使得类方法的代码更简单。
静态方法也可以实现定义多个初始化函数的功能,但静态方法每次都要写上类的名字,不方便,同时防止类名修改后,需要在方法定义中修改原来的类名称。
class Student(object):
def __init__(self, name, home='beijing'):
self.name = name
def __str__(self):
return self.name
@classmethod
def create(cls, name):
student = cls(name)
return student
s1 = Student('yuziyue')
s2 = Student.create('yuchaoshui')
print(s1.name)
print(s2.name)
静态方法
静态方法用 @classmethod
装饰,方法内不能访问实例属性,参数不能传入 self
和 cls
, 调用时可以用类或者实例调用。使用场景: 当类中某个方法不依赖实例时,避免传入 self
实例消耗资源成本(内存等)。
class Student(object):
school = "beijing university"
def __init__(self, name, home='beijing'):
self.name = name
def __str__(self):
return self.name
@staticmethod
def get_school():
school = Student.school
return school
s = Student('yuziyue')
print(s.get_school())
print(Student.get_school())
访问限制
私有属性和方法
- 以
__
(双下划线) 开头的属性或者方法是私有属性或方法,只能类内部访问,不能被实例访问,不能被子类继承。对于对象来说,双下划线的私有属性或方法的名称变了,变成了_classname__privartename
, 比如s._Student__mom
可以通过这样的方式访问到数据,但是这是极力不允许的。 - 以
_
(单下划线)开头的属性或者方法也是私有属性或方法,但是它能像公有属性和方法一样可以被类内方法、对象访问,,但是我们通常不去这么做,这样做很危险。也可以被子类继承下去。 - 开头结尾都是双下划线的属性或方式是类的特殊成员,有特殊用途,比如
__init__(self)
。 - 单下划线和双下划线来命名私有属性, 到底哪种方式好呢? 大多数而言,应该让非公共名称以单下划线开头。但是,如果你清楚你的代码会涉及到子类, 并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。
- 有时候定义的一个变量和某个保留关键字冲突,这时候可以使用单下划线作为后缀,例如:lambda_ = 3
class Student(object):
def __init__(self, name, phone='', mom='', dad='', home='beijing'):
self.name = name
self.__mom = mom
self.__dad = dad
self._phone = phone
self._home = home
def __str__(self):
return self.name
def __bed(self):
bedstr = "{} is sleepping!".format(self.name)
print(bedstr)
def __info(self):
print("mom: {}".format(self.__mom))
print("dad: {}".format(self.__dad))
print("phone: {}".format(self._phone))
print("home: {}".format(self._home))
def _meeting(self):
meetingstr = "{} is in a meeting!".format(self.name)
print(meetingstr)
def get_status(self):
self.__bed()
self.__info()
self._meeting()
s = Student('yuziyue', '12345678901', 'zhang', 'yu')
s.get_status()
print(s._Student__mom)
公有属性和方法
公有属性和方法能够在类内被访问,也能够被实例访问。
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def get_name(self):
return self.name
s = Student('yuziyue')
print(s.get_name())
动态绑定属性和方法
Python动态语言可以随时给一个对象绑定一个属性或方法。这样使得对象的使用非常简单。记住,这种绑定只对当前对象有效,对其他实例无效。
实例动态绑定属性
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
s.home = 'beijing'
print(s.home)
实例动态绑定方法
from types import MethodType
def set_age(self, age):
self.age = age
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
s.set_age = MethodType(set_age, s)
s.set_age(23)
print(s.age)
类动态绑定属性,对该类的所有实例有效
school = 'beijing university'
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
Student.school = school
print(s.school)
类动态绑定方法,对该类的所有实例有效
def set_age(self, age):
self.age = age
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
Student.set_age = set_age
s.set_age(23)
print(s.age)
限制动态绑定的属性和方法
- 为了限制动态绑定的属性,可以使用
__slots__
类的属性来限制,这样以来只有在__slots__
中指定的属性才可以通过动态绑定。 __slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用- 对于主要是用来当成简单的数据结构的类而言,使用
__slots__
可以极大的减少实例所占的内存。__slots__
更多的是用来作为一个内存优化工具,而非防止限制给实例添加新的属性。
class Student(object):
__slots__ = ("name", "age")
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
s = Student('yuziyue')
s.age = 23
print(s.age)
创建可管理的属性
实例的属性除了访问、修改外还可能要对其进行类型检查或合法性验证,使用 @property
装饰器可以解决该问题。 注意:类的属性必须是私有方法(_age
或者 __age
,二选一,不能混合使用),调用时可当属性调用。在 __init__()
中,self.age = age
为什么会没有下划线? 因为想在初始化赋值时就对其进行合法性检验。 在普通实例方法中正常使用 self.age
这种格式调用 age
属性。
class Person(object):
def __init__(self, age):
self.age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if not (0 < age < 200):
raise ValueError('age must between 0 and 200')
if type(age) != int:
raise ValueError('age must be a num')
self.__age = age
@age.deleter
def age(self):
raise AttributeError("Can't delete attribute")
def status(self):
print(self.age)
yzy = Person(23)
print(yzy.age)
yzy.age = 24
print(yzy.age)
yzy.status()
如果对属性的检查、修改函数都已经写好了,也可以使用 property
函数来实现上面的功能。
class Person(object):
def __init__(self, age):
self.set_age(age)
def get_age(self):
return self.__age
def set_age(self, age):
if not (0 < age < 200):
raise ValueError('age must between 0 and 200')
if type(age) != int:
raise ValueError('age must be a num')
self.__age = age
def del_age(self):
raise AttributeError("Can't delete attribute")
age = property(get_age, set_age, del_age)
yzy = Person(23)
print(yzy.age)
yzy.age = 24
print(yzy.age)
抽象类的实现
如果想只在基类做一些方法名称的定义,具体的实现到子类实现,则可以使用抽象类,这种基类不能实例化,而子类继承的方法必须要有覆盖,否则报错,也就是说执行类型检查来确保子类实现了某些特定的方法。
import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
all_type='file'
@abc.abstractmethod #定义抽象方法,无需实现功能
def read(self):
'子类必须定义读功能'
pass
@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
'子类必须定义写功能'
pass
#class Txt(All_file):
# pass
#
#t1=Txt() #报错,子类没有定义抽象方法
class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')
class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('硬盘数据的读取方法')
def write(self):
print('硬盘数据的读取方法')
wenben=Txt()
yingpan=Sata()
#这样大家都是被归一化了,也就是一切皆文件的思想
wenben.read()
yingpan.write()
print(wenben.all_type)
print(yingpan.all_type)
继承
- 子类继承父类的属性、方法,如果子类需要更多属性、方法时,则需要子类自己新增
- 需要在子类中使用
super().__init__()
的方式初始化父类 - 如果子类的属性、方法不能满足子类时,则需要子类自己覆盖重写该属性、方法
- 父类的属性、方法中,
__
(双下划线)开头的不能继承,只能继承公有属性、_
(单下划线)开头的
class Animal(object):
def __init__(self,name):
self.name = name
def run(self):
print('Animal is running...')
class Dog(Animal):
def __init__(self,name):
super().__init__(name)
def run(self):
print('Dog is running...')
def eat(self):
print('Dog is eating bone...')
class Cat(Animal):
def __init__(self,name):
super().__init__(name)
def run(self):
print('Cat is running...')
def eat(self):
print('Cat is eating rats...')
a = Animal('animal')
d = Dog('dog')
c = Cat('cat')
print(a.name)
print(d.name)
print(c.name)
a.run()
d.run()
c.run()
多重继承
- 多重继承的常用方式: 在使用多重继承是要避免使用链式继承 (动物->肉食动物->飞行动物->鸟),因为这样继承关系往往会很复杂。在设计类的继承关系时,通常,主线都是单一继承下来的,如果要加"功能",通过多重继承即可,这种方式叫做
MixIn
,比如狗的主线是Mammal
, 其他 "功能" 就是RunnableMixIn, KeeperMixIn, LoyaltyMixIn
。
class Dog(Mammal, RunnableMixIn, KeeperMixIn, LoyaltyMixIn):
pass
- 多重继承中的
__mro__
列表: 在对象初始化时,需要寻找对象的属性所在的类,该实例根据它的类的一个__mro__
列表来判断属性怎么初始化,属性、方法的调用顺序。为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
mro 列表构造的三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
注意: 子类调用super()
时不一定是调用直接父类,而是直接从 mro 列表获取下一个父类,看下面的例子
class A():
def example(self):
print("A's example!")
super().example()
class B():
def example(self):
print("B's example!")
class C(A, B):
def example(self):
print("C's example!")
super().example()
c = C()
print(C.__mro__)
c.example()
# 结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
C's example!
A's example!
B's example!
类 A()
没有父类,但是它用了super().example()
,从这里可以看出了 mro 的作用, 调用顺序是:C、A、B
- 多重继承父类需要参数进行初始化:如果在多继承中子类初始化时,需要传递参数给多个父类初始化,这时就需要使用 默认参数 和 关键字 参数配合完成。 实例初始化时,使用关键字参数的方式调用。 子类
__init__()
中只负责初始化提供的默认参数列表,其他的参数通过kwargs
传递到mro
列表的下一个类进行初始化。 如果父类想知道子类的属性值,可以在子类初始化时显示传递到父类super().__init__(sub=sub, **kwargs)
, 父类用显示关键字接收__init__(self, sub='', **kwargs)
,下面是例子。
import inspect
class Baseclass(object):
def __init__(self, base='', **kwargs):
super().__init__(**kwargs)
self.base = base
def fbase(self):
print('Baseclass class')
class A(Baseclass):
def __init__(self, a='', **kwargs):
super().__init__(**kwargs)
self.a = a
def fa(self):
print('A class')
class B(Baseclass):
def __init__(self, b='', **kwargs):
super().__init__(**kwargs)
self.b = b
def fb(self):
print('B class')
class C(Baseclass):
def __init__(self, c='', sub='', **kwargs):
super().__init__(**kwargs)
self.c = c
print("I am father C, and I know sub is {}!".format(sub))
def fc(self):
print('C class')
class Subclass(A, B, C):
def __init__(self, sub='', **kwargs):
super().__init__(sub=sub, **kwargs)
self.sub = sub
def fsub(self):
print('Subclass class')
yuchaoshui = Subclass(base='1', a='2', b='3', c='4', sub='5')
print(Subclass.__mro__)
print(inspect.getmro(Subclass))
print(yuchaoshui.base)
print(yuchaoshui.a)
print(yuchaoshui.b)
print(yuchaoshui.c)
print(yuchaoshui.sub)
# 结果如下:
I am father C, and I know sub is 5!
(<class '__main__.Subclass'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Baseclass'>, <class 'object'>)
(<class '__main__.Subclass'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Baseclass'>, <class 'object'>)
1
2
3
4
5
多态
- 向不同的对象发送同一条消息,不同的对象在接收消息时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
- 实现方法
1、继承父类然后重载方法,子类和父类调用相同的方法,会有不同的结果。
2、实例作为参数传递给一个函数,在函数里面调用相同方法,会有不同的结果。
class AudioFile:
def __init__(self, filename):
if not filename.endswith(self.ext):
raise Exception("Invalid file format")
self.filename = filename
class MP3File(AudioFile):
ext = ".mp3"
def play(self):
print("Playing {} as mp3".format(self.filename))
class WavFile(AudioFile):
ext = ".wav"
def play(self):
print("Playing {} as wav".format(self.filename))
class OggFile(AudioFile):
ext = ".ogg"
def play(self):
print("Playing {} as ogg".format(self.filename))
class FlacFile:
def __init__(self, filename):
if not filename.endswith(".flac"):
raise Exception("Invalid file format")
self.filename = filename
def play(self):
print("Playing {} as flac".format(self.filename))
mp3 = MP3File("file.mp3")
wav = WavFile("file.wav")
ogg = OggFile("file.ogg")
flac = FlacFile("file.flac")
mp3.play()
wav.play()
ogg.play()
flac.play()
特殊属性特殊方法
dir()
函数可以打印出一个对象的所有属性、方法。返回一个 list,比如 print(dir(instancename))
。
__dict__
保存对象的所有属性,是一个字典,key
是属性名,value
是属性值。__dir__()
当 全局函数dir()
使用时调用该方法。返回值是一个list,存储该对象的所有属性、方法。__slots__
限制动态给实例添加的属性。__slots__
更多的是用来作为一个内存优化工具。__slots__
属性来极大的减少实例所占的内存。当在创建大量的实例时,可以使用这种方法,但是一旦使用了__slots__
实例的很多特性都不能使用了。__str__()
返回实例的默认打印属性。__repr__()
同上,不同点是__repr__
是给调试人员使用的。__iter__()
如果一个类想被用于for ... in
循环,类似list
或tuple
那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象。__getitem__()
实现该方法,可以像list
那样使用访问元素的第几位,比如Fib(10)[5]
。__setitem__()
实现该方法,把对象视作list或dict来对集合赋值。Fib(10)[5] = 3
。__delitem__()
实现该方法, 用于删除某个元素。__getattribute__
获取实例属性时调用该函数(属性、方法),看下面示例。
class A:
def __init__(self, name):
self.name = name
def sleep(self):
print("{} is sleeping.".format(self.name))
def eat(self):
print("{} is eating.".format(self.name))
@classmethod
def life(cls):
print("everyone has a life!")
def __getattribute__(self, attribute_name):
print("__getattribute__ is called!")
return super().__getattribute__(attribute_name)
a = A('yuziyue')
print(a.name)
print()
a.sleep()
print()
a.eat()
print()
A.life()
__getattr__()
当实例获取不存在的属性时会调用该方法, 返回该方法的返回值,可以返回一个值,也可以返回函数。__call__()
定义该方法后,可以在实例后面加括号当函数来调用,调用的就是__call__()
方法。__format__()
该方法接收一个需要格式化的格式返回一个格式化好的字符串,最终当使用format
格式化对象时,调用对象的该方法。比如format(d, 'ymd')
。__new__()
是构造函数, 实例化对象时,先调用它实例化一个对象,然后才调用__init__()
初始化对象的属性值。__enter__()
让对象支持with
语句时, 要实现该方法。__exit()__
让对象支持with
语句时, 要实现该方法。with
语句块结束是调用该方法。__del__()
该方法是当一个对象的生命周期结束时被调用,做清理工作。__get__() 、__set__() 、 __delete__()
拥有这三个函数的类就是一个描述器,用于对类实例的属性进行合法性检查。
元类
元类是类的一种。正如类定义了实例的功能,元类也定义了类的功能。类是元类的实例。
使用 type 元类创建类
通过type()
函数创建的类和直接写class
是完全一样的, 要创建一个class
对象,type()
函数依次传入3个参数
a. class
的名称;
b. 继承的父类集合,以tuple
的形式给出;
c. class
的方法名称与函数绑定。
(下面只是一个例子,平时并不会这么使用)
def __init__(self):
self.name = 'noboby'
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
Person = type('Person', (object,), dict(get_name=get_name, set_name=set_name, __init__=__init__))
p = Person()
print(p)
print(p.name)
p.set_name('yzy')
print(p.name)
print(p.get_name())
# 结果如下:
<__main__.Person object at 0x7f18bf75a6d8>
noboby
yzy
yzy
自定义的Mylist增加add方法
cls、name、 bases、 attrs
分别表示:当前创建的类、 类的名字、类继承的父类集合、类的方法集合。
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class Mylist(list, metaclass=ListMetaclass):
pass
urls = Mylist()
urls.add("http://www.baidu.com/")
urls.add("http://www.kekenet.com/")
print(urls)
利用元类编写简单 ORM 框架
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<%s:%s>' % (self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint')
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings
attrs['__table__'] = name
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
class User(Model):
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()
使用元类优雅地实现单例模式
class Singleton(type):
def __init__(self, *args, **kwargs):
self.__instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super().__call__(*args, **kwargs)
return self.__instance
class Spam(metaclass=Singleton):
def __init__(self):
print('Creating Spam')
s = Spam()
s1 = Spam()
print(s is s1)
单例模式表示该类只能有一个实例,type
表示元类,Singleton
继承至元类。 __call__()
表示它所在的类使用括号()
时被调用,Spam
是 Singleton
的一个实例,s、s1
是Spam
的实例。
如果不使用元类,则必须要使用一个全局变量来保存实例了。
class Singleton:
def __init__(self):
print('Creating.')
settings_instance = None
def Settings():
global settings_instance
if settings_instance is None:
settings_instance = Singleton()
return settings_instance
s = Settings()
s1 = Settings()
print(s is s1)
或者使用类的静态方法,看下面示例:
class Foo(object):
__instance = None
@staticmethod
def singleton():
if Foo.__instance:
return Foo.__instance
else:
Foo.__instance = Foo()
return Foo.__instance
f = Foo.singleton()
f1 = Foo.singleton()
print(f is f1)
但后面两种方法并不是那么优雅,哈哈哈哈。
在类上强制使用编程规约
如果希望强制执行某些编程规约来帮助程序员保持清醒,比如继承的子类类属性
、方法名
不允许大小写混写。
class NoMixedCaseMeta(type):
def __new__(cls, clsname, bases, clsdict):
for name in clsdict:
if name.lower() != name:
raise TypeError('Bad attribute name: ' + name)
return super().__new__(cls, clsname, bases, clsdict)
class Root(metaclass=NoMixedCaseMeta):
pass
class A(Root):
def foo_bar(self):
pass
class B(Root):
def foobar(self):
pass
class C(Root):
School = 'beijing university'
def __init__(self):
pass
a=A()
b=B()
c=C()