第七天 反射 面向对象 异常
反射:
1 # 反射:通过字符串的形式,导入模块;通过字符串的形式,去模块中寻找指定函数,并执行 2 # python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员 3 import commons 4 5 commons内容: 6 def f1(): 7 print('F1') 8 return 233 9 def f2(): 10 print('F2') 11 def d3(): 12 print('F3') 13 NAME = 11211 14 15 # 根据用户输入导入模块: 16 inp = input('请输入模块:') 17 print(inp, type(inp)) 18 19 dd = __import__(inp) # dd = __import__('commons') # __import__导入方法,必须用别名的方式,否则报错 20 dd.f1() # 输出F1 21 22 # 应用:根据用户输入的内容,来导入模块 23 inp = input('请输入模块:') 24 inp_func = input('请输入要执行的函数:') 25 dd = __import__(inp) 26 target_func = getattr(dd, inp_func) # getattr用于以字符串形式去某个模块中寻找函数 27 target_func1 = getattr(dd, 'NAME', None) # None 设置默认值,如果NAME为空,则target_func为None 28 29 result = target_func() 30 print(result) 31 print(target_func1) 32 33 34 # 根据字符串的形式去对象(某个模块)中操作其成员 35 36 target_func = getattr(commons, 'NAME', None) # 37 print(target_func) 38 39 r = hasattr(commons, 'NAME') 40 print(r) 41 42 r = hasattr(commons, 'ASD') 43 print(r) 44 # 通过setattr设置模块中某个变量 45 setattr(commons, 'ASD', lambda a: a + 1) 46 r = hasattr(commons, 'ASD') 47 print(r) 48 49 50 # 通过delattr删除模块中某个变量 51 delattr(commons, 'ASD') 52 r = hasattr(commons, 'ASD') 53 print(r) 54 55 mod = getattr(commons, 'NAME', 5566) 56 print(mod) # 当commons模块中存在NAME变量时,输出其值;不存在,值为5566 57 58 # 小结反射: 59 # 根据字符串的形式取某个模块中寻找东西 60 # 根据字符串的形式取某个模块中判断东西是否存在 61 # 根据字符串的形式去某个模中设置东西 62 # 根据字符串的形式取某个模块中删除的东西 63 # 根据字符串的形式去对象(某个模块)中操作其成员 64 65 # 扩展:__import__ 66 # 单层 67 r = __import__("模块名") 68 # 针对该模块同一级下有另一个目录,也就是包,而这个包下有另一个包,而我们需要导入的模块还在其下面,这时候,不能应用包.包.模块作为字符串传入__import__来导入了,因为其只会导入第一层包。需要加入一个参数 fromlist=True 69 q = __import__('a.b.c.login', fromlist=True) 70 71 72 73 74 # 基于反射模拟web框架路由系统 75 76 # 初级版: 77 # from lib import account 78 DD = __import__('lib.account', fromlist=True) # 当前py文件和lib目录在同一级目录下 79 url = input('请输入url: ') 80 81 if url.endswith('login'): 82 r = DD.login() 83 print(r) 84 elif url.endswith('logout'): 85 r = DD.logout() 86 print(r) 87 elif url.endswith('nb'): 88 r = DD.nb() 89 print(r) 90 else: 91 print('404') 92 93 94 #优化版:在url中利用反射调用模块中的函数 95 # from lib import account 96 DD = __import__('lib.account', fromlist=True) # 当前py文件和lib目录在同一级目录下 97 98 url = input('请输入url:') 99 inp = url.split('/')[-1] # 字符串的拆分,拆开后结果为列表 100 if hasattr(DD, inp): 101 target_func = getattr(DD, inp) 102 r = target_func() 103 print(r) 104 else: 105 print(404) 106 107 108 109 # 通过反射导入模块,并通过反射来找到模块里面的某个函数并且执行: 110 # 规则:输入的url为:域名+模块名+函数名 111 url = input('请输入url:') 112 target_moduel, target_func = url.split('/')[-2:] 113 114 m = __import__('lib.'+target_moduel, fromlist=True) 115 116 if hasattr(m, target_func): 117 target_func = getattr(m, target_func) 118 r = target_func() 119 print(r) 120 else: 121 print(404)
面向对象:
入门举例:
1 class Whisky: 2 3 def fetch(self, backend): 4 print('this is :', backend) 5 6 def add_record(self, backend, record): 7 print(backend, record) 8 9 obj = Whisky() 10 obj.fetch('www.oldboy.com') 11 obj.add_record('www.oldboy.com', '192.168.2.1') 12 #输出 13 # this is : www.oldboy.com 14 # www.oldboy.com 192.168.2.1
面向对象三大特性之一:封装
self详解:
1 # self详解 2 class Whisky: 3 4 def fetch(self, backend): 5 print(self, backend) # <__main__.Whisky object at 0x0000000000BAE400> www.baidu.com 6 7 def add_record(self, backend, record): 8 pass 9 10 obj = Whisky() 11 obj.fetch('www.baidu.com') 12 print(obj) # <__main__.Whisky object at 0x0000000000BAE400> 13 # 由上可知,self是对象自身
利用__init__内置方法(加载类的时候会自动执行)来封装:
1 1. 通过self间接调用被封装的内容 2 class Foo: 3 def __init__(self, bk, time, place): 4 self.name = 'whisky' # __init__初始化信息 5 self.favor = bk 6 self.time = time 7 self.place = place 8 9 def sister(self): 10 print('i am your sister') 11 print('i work in %s' % self.place) 12 13 def brother(self): 14 print('i am your little brother') 15 print('i work in %s' % self.place) 16 obj = Foo('women', '0912', 'SZ') # 类传参 17 print(obj.name) # whisky 18 print(obj.favor) # women 19 print(obj.place) # SZ 20 21 obj.sister() 22 obj.brother() 23 # 输出 24 # i am your sister 25 # i work in SZ 26 # i am your little brother 27 # i work in SZ 28 29 30 31 # 2、通过对象直接调用被封装的内容 32 33 class Foo: 34 35 def __init__(self, name, age): 36 self.name = name 37 self.age = age 38 39 40 obj1 = Foo('whisky', '25') 41 print(obj1.name, obj1.age) 42 43 obj2 = Foo('alex', '32') 44 print(obj2.name, obj2.age) 45 46 # 输出 47 # whisky 25 48 # alex 32
小结
1.对于面向对象的封装来说,其实就是使用构造方法将相同内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
2.封装使用场景:当同一类型的方法具有相同的参数时,直接封装(用__init__构造方法)到对象里面即可
3.类+括号,自动执行类中的__init__方法,__init__又称为构造方法,在其中执行具体封装的操作
4.__del__ 解释器销毁对象时候自动调用,又称为 析构方法
练习举例:
1 class Person: 2 3 def __init__(self, name, age, weight): 4 self.name = name 5 self.age = age 6 self.weight = int(weight) 7 8 def chi(self): 9 self.weight += 2 10 11 def exercise(self): 12 self.weight -= 1 13 14 15 obj = Person('whisky', '25', '166') 16 17 obj.chi() 18 obj.chi() 19 obj.chi() 20 obj.exercise() 21 print(obj.weight) 22 # 输出 171 23 24 25 26 练习一:在终端输出如下信息 27 28 小明,10岁,男,上山去砍柴 29 小明,10岁,男,开车去东北 30 小明,10岁,男,最爱disco 31 老李,90岁,男,上山去砍柴 32 老李,90岁,男,开车去东北 33 老李,90岁,男,最爱disco 34 35 class People: 36 37 def __init__(self, name, age, gender): 38 self.name = name 39 self.age = age 40 self.gender = gender 41 42 def cut(self): 43 print('%s, %s, %s,上山去砍柴' % (self.name, self.age, self.gender)) 44 45 def go_to_north(self): 46 print('%s, %s, %s,开车去东北' % (self.name, self.age, self.gender)) 47 48 def go_big_sord(self): 49 print('%s, %s, %s,最爱disco' % (self.name, self.age, self.gender)) 50 51 xiaoming = People('小明', '10岁', '男') 52 xiaoming.cut() 53 xiaoming.go_to_north() 54 xiaoming.go_big_sord() 55 56 laoli = People('老李', '90岁', '男') 57 laoli.cut() 58 laoli.go_to_north() 59 laoli.go_big_sord() 60 61 62 63 64 练习二:游戏人生程序 65 66 1、创建三个游戏人物,分别是: 67 68 苍井井,女,18,初始战斗力1000 69 东尼木木,男,20,初始战斗力1800 70 波多多,女,19,初始战斗力2500 71 2、游戏场景,分别: 72 73 草丛战斗,消耗200战斗力 74 自我修炼,增长100战斗力 75 多人游戏,消耗500战斗力 76 77 78 79 class Character: 80 def __init__(self, name, gender, age, value): 81 self.name = name 82 self.gender = gender 83 self.age = int(age) 84 self.value = int(value) 85 86 def grass_fight(self): 87 self.value -= 200 88 89 def self_fight(self): 90 self.value += 100 91 92 def multi_player(self): 93 self.value -= 500 94 95 def detail(self): 96 print('your character info: name:%s gender:%s age:%s value:%s' % (self.name, self.gender, self.age, self.value)) 97 98 99 cang = Character('苍井井', '女', '18', '1000') 100 dong = Character('东尼木木', '男', '20', '1800') 101 bo = Character('波多多', '女', '19', '2500') 102 103 cang.grass_fight() 104 cang.detail()
继承
1 # 对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。 2 # 除了 子类 和 父类 的称谓,还有 派生类 和 基类 ,他们与子类和父类只是叫法不同而已 3 4 # 简单示例: 5 6 class Animals: 7 def chi(self): 8 print(self.name + ' 吃') 9 10 def he(self): 11 print(self.name + '喝') 12 13 class Cat(Animals): 14 def __init__(self, name): 15 self.name = name 16 17 def cry(self): 18 print('喵') 19 20 class Dog(Animals): 21 def __init__(self, name): 22 self.name = name 23 24 def cry(self): 25 print('汪') 26 27 28 alex = Dog('lijie') 29 alex.cry() # 汪 30 alex.chi() # lijie 吃 31 32 33 34 # 注意: 35 # 父类也叫基类,子类也叫派生类,派生类可以继承父类中所有的功能 36 # 父类也可向上继承父类,父类,子类都是相对的,子类也可继承父类的父类 37 # 子类父类命名相同的一个方法,优先寻找子类中的方法,并执行 38 # 在Java中一个子类只能继承一个父类,python一个子类可以继承多个父类,优先级:从左至右
多继承:
1 class Human: 2 def eat(self): 3 print(self.name + '吃') 4 5 def drink(self): 6 print(self.name + 'drink') 7 8 def shit(self): 9 print(self.name + 'shit') 10 11 12 class Family: 13 def gamble(self): 14 print('gamble') 15 16 def tour(self): 17 print('tourism') 18 19 def eat(self): 20 print(self.name + 'eat....') 21 22 class Dog(Human, Family): 23 def __init__(self, name): 24 self.name = name 25 26 badi = Dog('badi') 27 badi.eat()
多继承时查找顺序:
注意:Python 2.x中默认都是经典类,只有显式继承了object才是新式类
Python 3.x中默认都是新式类,不必显式的继承object
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找

如上图的继承关系的查找顺序:
1 # 经典类:深度优先 2 class D: 3 def bar(self): 4 print('class D') 5 6 7 class C(D): 8 def bar(self): 9 print('class C') 10 11 12 class B(D): 13 # def bar(self): 14 # print(B.bar) 15 pass 16 17 18 class A(B, C): 19 # def bar(self): 20 # print(A.bar) 21 pass 22 23 a = A() 24 a.bar() 25 26 #python2.7经典类 输出 class D 27 #python3.5新式类 输出 class C

如上图的继承关系的查找顺序:
1 class F: 2 def bar(self): 3 print('class F') 4 5 6 class E(F): 7 pass 8 # def bar(self): 9 # print(E.bar) 10 11 12 class D(F): 13 pass 14 # def bar(self): 15 # print('class D') 16 17 18 class C(E): 19 def bar(self): 20 print('class C') 21 22 class B(D): 23 pass 24 # def bar(self): 25 # print(B.bar) 26 27 28 class A(B, C): 29 # def bar(self): 30 # print(A.bar) 31 pass 32 33 a = A() 34 a.bar() 35 36 #输出: 37 # python3.5 输出 class C 38 # python2.7 输出 class F

没有共同基类时,python2/3都会按深度优先方式查找,有共同基类的多继承情况下,python2(经典类)深度优先,python3(新式类)广度优先
2 3 class E: 4 def bar(self): 5 print(E.bar) 6 7 8 class D: 9 def bar(self): 10 print('class D') 11 12 13 class C(E): 14 def bar(self): 15 print('class C') 16 17 class B(D): 18 pass 19 # def bar(self): 20 # print(B.bar) 21 22 23 class A(B, C): 24 # def bar(self): 25 # print(A.bar) 26 pass 27 28 a = A() 29 a.bar() 30 31 #输出: 32 # python3.5 输出 class D 33 # python2.7 输出 class D
小结:
记法:只有当满足:python3新式类、有共同基类的情况下,才会出现BFS;
经典类多继承属性搜索顺序: 先深入继承树左侧,再返回,开始找右侧;
新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动
多继承易错点:
1 # 易错:调用多继承中嵌套方法中的方法,执行顺序是? 2 class A: 3 def bar(self): 4 print('BAR') 5 self.f1() # self是d1这个对象,self.f1()相当于d1这个对象去调用f1 6 7 def f1(self): 8 print('B') 9 10 11 class B(A): 12 def f1(self): 13 print('B') 14 15 16 class C: 17 def f1(self): 18 print('C') 19 20 21 class D(C, B): 22 pass 23 24 d1 = D() 25 d1.bar() 26 27 # 输出: 28 # BAR 29 # C 30 31 b = B() 32 b.bar() 33 # 输出: 34 # BAR 35 # B 36 37 a = A() 38 a.bar() 39 # 输出: 40 # BAR 41 # B 42 43 # 小结:多继承中,注意方法是从哪里、哪个对象发起的,类中调用另外一个类中的方法时,找方法时要从发起者(对象是谁),最初是哪个类实例化的对象,就去哪个类中按照继承顺序开始找调用的方法
多态:
1 # 多态: 2 3 class Foo: 4 def f1(self): 5 print('Foo') 6 7 8 class Bar: 9 def f1(self): 10 print('Bar') 11 12 13 def func(arg): 14 arg.f1() 15 16 func(Foo()) # 类也可当做参数来传递 17 func(Bar()) 18 19 # 输出: 20 # Foo 21 # Bar
# 多态以 继承 和 重写 父类方法 为前提, 多态是调用方法的技巧,不会影响到类的内部设计, # 多态, 不同的 子类对象调用 相同的 父类方法,产生 相同/不同的 执行结果,可以增加代码的外部 调用灵活度, class Person(object): def __init__(self, name, sex): self.name = name self.sex = sex def print_title(self): if self.sex == "male": print("man") elif self.sex == "female": print("woman") class Child(Person): # Child 继承 Person def print_title(self): if self.sex == "male": print("boy") elif self.sex == "female": print("girl") May = Child("May", "female") Peter = Person("Peter", "male") print(May.name, May.sex, Peter.name, Peter.sex) May.print_title() Peter.print_title() # 多态的好处就是,当我们需要传入更多的子类,例如新增 Teenagers、Grownups 等时,我们只需要继承 Person 类型就可以了, # 而print_title()方法既可以不重写(即使用Person父类的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节, # 而当我们新增一种Person的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的“开闭”原则: # 对扩展开放(Open for extension):允许子类重写方法函数 # 对修改封闭(Closed for modification):不重写,直接继承父类方法函数 # 多态有点:增加程序的灵活性、可扩展性 # 多态, 不同的 子类对象调用 相同的 父类方法,产生 相同/不同的 执行结果,可以增加代码的外部 调用灵活度,方便扩展功能 # 多态以 继承 和 重写 父类方法 为前提, 多态是调用方法的技巧,不会影响到类的内部设计 class Dog(object): def work(self): pass class ArmyDog(Dog): def work(self): print('追击敌人') class DrugDog(Dog): def work(self): print('追查毒品') class Person(object): def work_with_dog(self, dog): # 只要能接收父类对象,就能接收子类对象 # 不同的子类对象调用相同的父类方法 dog.work() # 只要父类对象能工作,子类对象就能工作。并且不同子类会产生不同的执行效果,也可以用父类中的方法,子类中不定义。 p = Person() p.work_with_dog(ArmyDog()) p.work_with_dog(DrugDog())
class Animal: def kind(self): print("i am animal") class Dog(Animal): def kind(self): print("i am a dog") class Cat(Animal): def kind(self): print("i am a cat") class Pig(Animal): def kind(self): print("i am a pig") # 这个函数接收一个animal参数,并调用它的kind方法 def show_kind(animal): animal.kind() d = Dog() c = Cat() p = Pig() show_kind(d) show_kind(c) show_kind(p) ------------------ 打印结果: i am a dog i am a cat i am a pig # 狗、猫、猪都继承了动物类,并各自重写了kind方法。show_kind()函数接收一个animal参数,并调用它的kind方法。可以看出,无论我们给animal传递的是狗、猫还是猪,都能正确的调用相应的方法,打印对应的信息。这就是多态。 # 如果你学过JAVA这一类强类型静态语言,就不会这么觉得了,对于JAVA,必须指定函数参数的数据类型,只能传递对应参数类型或其子类型的参数,不能传递其它类型的参数,show_kind()函数只能接收animal、dog、cat和pig类型,而不能接收job类型。就算接收dog、cat和pig类型,也是通过面向对象的多态机制实现的。
super 方法:执行父类的构造方法、方法(super不能在python2中的经典类中使用,要在python2中声明新式类)
1 class Animal: 2 def __init__(self): 3 print('A 构造方法') 4 self.ty = '动物' 5 6 7 class Cat(Animal): 8 def __init__(self): 9 print('B构造方法') 10 self.n = '猫' 11 super(Cat, self).__init__() # 此处self为c对象,super会自动把self传给init,所以init的括号中不需再写self 12 # Animal.__init__(self) # 注释部分是第二种执行父类构造方法的方法,推荐使用super的方法 13 14 c = Cat() 15 print(c.__dict__)
输出:
B构造方法
A 构造方法
{'ty': '动物', 'n': '猫'}
1 class Vehicle1: 2 Country='China' 3 def __init__(self,name,speed,load,power): 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 def run(self): 9 print('开动啦') 10 print('开动啦') 11 class Subway(Vehicle1): 12 def __init__(self,name,speed,load,power,line): 13 # Vehicle.__init__(self,name,speed,load,power) # 用了super方法取代这种需要根据父类名字继承父类的方式 15 super().__init__(name,speed,load,power) 等价于 # super(Subway,self).__init__(name,speed,load,power) (python2中不支持省略的写法) 16 self.line=line 17 def show_info(self): 18 print(self.name,self.speed,self.load,self.power,self.line) 19 def run(self): 20 # Vehicle.run(self) 21 super().run() # 调用父类的run方法 22 print('%s %s 线,开动啦' %(self.name,self.line)) 23 line13=Subway('北京地铁','10km/s',1000000000,'电',13) 24 line13.show_info() 25 line13.run() 26 27 print(line13.__class__) 28 29 30 31 super()调用父类方法好处: 32 1、不用写父类的名字 33 2、不用传self参数,默认传进去了
# 为什么要重载父类方法: 让子类的对象具有父类的构造属性
# 若父类构造函数包含很多属性,子类仅需新增1、2个,会有不少冗余的代码: class Person(object): def __init__(self,name,sex): self.name = name self.sex = sex print('init father') class Child(Person): # Child 继承 Person def __init__(self,name,sex,mother,father): self.name = name self.sex = sex self.mother = mother self.father = father print('init child') May = Child("May","female","April","June") print(May.name,May.sex,May.mother,May.father) #这边,子类可对父类的构造方法进行调用,参考如下: class Person(object): def __init__(self,name,sex): self.name = name self.sex = sex print("in father init") class Child(Person): def __init__(self,name,sex,mother,father): super(Child, self).__init__(name,sex) # 子类对父类的构造方法的调用 self.mother = mother self.father = father print("in child init ") May = Child("May","female","April","June") print(May.name,May.sex,May.mother,May.father)
hasattr方法之反射:利用反射查找面向对象成员归属
1 通过类可以直接找其中的某个类成员(hasattr); 2 通过对象,既可以找对象中的成员,又可以找类中的成员 3 4 5 class Foo: 6 7 def __init__(self, name): 8 self.name = name 9 10 def show(self): 11 print('show') 12 13 obj = Foo('whisky') 14 r = hasattr(Foo, 'show') 15 print(r) 16 r = hasattr(Foo, 'name') 17 print(r) 18 # 输出 19 # True 20 # False
利用反射导入模块、查找类、创建对象、查找对象中的字段:
1 同一路径下: 2 0912.py 3 4 class Foo: 5 6 def __init__(self, name): 7 temp = 'xxx' 8 self.name = name 9 10 def show(self): 11 print('show')
1 0914.py 2 3 # 导入模块 4 m = __import__('0912', fromlist=True) 5 6 # 去模块中找类 7 class_name = getattr(m, 'Foo') 8 9 # 根据类创建对象 10 obj = class_name('whisky') 11 12 # 去对象中找name对应的值 13 val = getattr(obj, 'name') 14 print(val) # 输出 whisky
类的成员

静态字段:
(存在意义:将每一个对象中重复的东西,只在类中保存一份,即称为静态字段。但如果放在__init__普通字段中,每个实例化出来的对象中都要保存一份)
1 class Province: 2 country = '中国' # 静态字段 3 4 def __init__(self, name): 5 temp = 'xxx' # 普通字段,对象(中) 6 self.name = name # 普通字段,在对象中 7 8 def show(self): # 普通方法(类中) 9 print('show') 10 print(self.country) 11 12 13 # 直接访问普通字段 14 obj = Province('河北省') 15 print(obj.name) # 输出 河北省 16 17 # 直接访问静态字段 18 r = Province.country 19 print(r) # 输出 中国 20 21 # 静态字段在类中,就用类去访问;普通字段在对象中,就用对象去访问,尽量自己去访问属于自己的成员,但是类中的方法,不要用类去访问 22 # 静态字段在内存中只保存一份 23 # 普通字段在每个对象中都要保存一份 24 # 应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

静态方法, 类方法(是静态方法的特殊情况,比静态方法多传递了一个类名参数):
# 静态方法存在的意义:不需要创建对象,不需传递self,可以不带参数,就可以访问这个方法,其实就是函数的功能,无需使用对象封装的内容,和对象没关系了
# 静态方法通过类调用,通过对象也可以访问,访问规则:通过类访问-》静态字段、静态方法、类方法; 通过对象访问-》普通字段、类的方法,不到万不得已,不要乱调用
1 class Foo: 2 country = '中国' 3 4 def __init__(self, name): 5 temp = 'xxx' 6 self.name = name 7 8 9 def common(self): 10 print('common method') 11 12 @staticmethod 13 def xo(): #静态方法可以不带任何参数,没有self,无法传递对象 14 print('xo') 15 16 @classmethod 17 def feed(cls): #类方法,至少要有一个cls参数,传递类名 18 print('feed', cls) 19 20 # 调用普通方法: 21 f = Foo('whisky') 22 f.common() # 输出 common method 23 24 # 调用静态方法 25 Foo.xo() # 输出 xo ,通过类调用静态方法 26 27 # 调用类方法 28 Foo.feed() # 输出 feed <class '__main__.Province'> 类名
属性:
property:特性,将方法伪造成字段的形式访问,效果:调用时无需加括号:
1 class Foo: 2 def __init__(self, name): 3 self.name = name 4 5 def start(self): 6 temp = '%s sb' % self.name 7 return temp 8 @property # 定义属性 9 def end(self): 10 temp = '%s sb' % self.name 11 return temp 12 13 obj = Foo('lilei') 14 ret1 = obj.start() # 调用方法 15 ret2 = obj.end # 调用属性 16 print(ret1) # lilei sb 17 print(ret2) # lilei sb 18 19 #由属性的定义和调用要注意一下几点: 20 # 定义时,在普通方法的基础上添加 @property 装饰器; 21 # 定义时,属性仅有一个self参数 22 # 调用时,无需括号 23 # 注意:1.属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象 24 # 2.属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
1 # 实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括: 2 # 3 # 根据用户请求的当前页和总数据条数计算出 m 和 n 4 # 根据m 和 n 去数据库中请求数据 5 # ############### 定义 ############### 6 class Pager: 7 8 def __init__(self, current_page): 9 # 用户当前请求的页码(第一页、第二页...) 10 self.current_page = current_page 11 # 每页默认显示10条数据 12 self.per_items = 10 13 14 15 @property # 完全可以去掉property,调用start方法实现相同的功能 16 def start(self): 17 val = (self.current_page - 1) * self.per_items 18 return val 19 20 @property 21 def end(self): 22 val = self.current_page * self.per_items 23 return val 24 25 # ############### 调用 ############### 26 27 p = Pager(1) 28 a = p.start # 就是起始值,即:m 29 b = p.end # 就是结束值,即:n 30 print(a, b) # 输出 0 10 31 # 从上述可见,Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。
在新式类中,具有三种@property装饰器:
setter 设置property特性属性的值,deleter 删除property特性属性的值,用得不广泛,但要看懂
1 class Foo: 2 def __init__(self, name): 3 self.name = name 4 5 def start(self): 6 temp = '%s sb' % self.name 7 return temp 8 9 @property # 定义属性,相当于把方法变为字段,调用end方法时不用加括号访问 10 def end(self): 11 temp = '%s hero' % self.name 12 return temp 13 14 @end.setter 15 def end(self, value): # 注意:装饰器中方法的名称要相同,示例中都是end 16 print(value) 17 self.name = value 18 19 @end.deleter 20 def end(self): 21 pass 22 # ############### 调用 ############### 23 24 obj = Foo('whisky') 25 print(obj.name) # whisky 26 print(obj.end) # whisky hero , 自动执行 @property 修饰的 end 方法,并获取方法的返回值 27 obj.name = '123' 28 print(obj.name) # 123 , 自动执行 @end.setter 修饰的 end 方法,并将 123 赋值给方法的参数 29 del obj.end # 自动执行 @end.deleter 修饰的 price 方法 30 print(obj.name) # 123 31 32 # 注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法 33 # 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
1 class Goods(object): 2 3 def __init__(self): 4 # 原价 5 self.original_price = 100 6 # 折扣 7 self.discount = 0.8 8 9 @property 10 def price(self): 11 # 实际价格 = 原价 * 折扣 12 new_price = self.original_price * self.discount 13 return new_price 14 15 @price.setter 16 def price(self, value): 17 self.original_price = value 18 19 @price.deleter 20 def price(self): 21 del self.original_price #删除了self.original_price这个属性 22 obj = Goods() 23 r = obj.price # 获取商品价格 24 print(r) 25 obj.price = 200 # 修改商品原价 26 del obj.price # 删除商品原价
属性的两种定义方式之二--静态字段方式
1 class Foo: 2 3 def get_bar(self): 4 return 'whisky' 5 6 BAR = property(get_bar) 7 8 obj = Foo() 9 reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值 10 print(reuslt) 11 12 # property的构造方法中有个四个参数 13 # 第一个参数是方法名,调用 对象.属性 时自动触发执行方法 14 # 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法 15 # 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法 16 # 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
1 class Goods(object): 2 3 def __init__(self): 4 # 原价 5 self.original_price = 100 6 # 折扣 7 self.discount = 0.8 8 9 def get_price(self): 10 # 实际价格 = 原价 * 折扣 11 new_price = self.original_price * self.discount 12 return new_price 13 14 def set_price(self, value): 15 self.original_price = value 16 17 def del_price(self): 18 del self.original_price 19 20 PRICE = property(get_price, set_price, del_price, '价格属性描述...') 21 22 obj = Goods() 23 obj.PRICE # 获取商品价格 24 obj.PRICE = 200 # 修改商品原价 25 del obj.PRICE # 删除商品原价
注意:Python WEB框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性
1 class WSGIRequest(http.HttpRequest): 2 def __init__(self, environ): 3 script_name = get_script_name(environ) 4 path_info = get_path_info(environ) 5 if not path_info: 6 # Sometimes PATH_INFO exists, but is empty (e.g. accessing 7 # the SCRIPT_NAME URL without a trailing slash). We really need to 8 # operate as if they'd requested '/'. Not amazingly nice to force 9 # the path like this, but should be harmless. 10 path_info = '/' 11 self.environ = environ 12 self.path_info = path_info 13 self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/')) 14 self.META = environ 15 self.META['PATH_INFO'] = path_info 16 self.META['SCRIPT_NAME'] = script_name 17 self.method = environ['REQUEST_METHOD'].upper() 18 _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', '')) 19 if 'charset' in content_params: 20 try: 21 codecs.lookup(content_params['charset']) 22 except LookupError: 23 pass 24 else: 25 self.encoding = content_params['charset'] 26 self._post_parse_error = False 27 try: 28 content_length = int(environ.get('CONTENT_LENGTH')) 29 except (ValueError, TypeError): 30 content_length = 0 31 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) 32 self._read_started = False 33 self.resolver_match = None 34 35 def _get_scheme(self): 36 return self.environ.get('wsgi.url_scheme') 37 38 def _get_request(self): 39 warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or ' 40 '`request.POST` instead.', RemovedInDjango19Warning, 2) 41 if not hasattr(self, '_request'): 42 self._request = datastructures.MergeDict(self.POST, self.GET) 43 return self._request 44 45 @cached_property 46 def GET(self): 47 # The WSGI spec says 'QUERY_STRING' may be absent. 48 raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '') 49 return http.QueryDict(raw_query_string, encoding=self._encoding) 50 51 # ############### 看这里看这里 ############### 52 def _get_post(self): 53 if not hasattr(self, '_post'): 54 self._load_post_and_files() 55 return self._post 56 57 # ############### 看这里看这里 ############### 58 def _set_post(self, post): 59 self._post = post 60 61 @cached_property 62 def COOKIES(self): 63 raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') 64 return http.parse_cookie(raw_cookie) 65 66 def _get_files(self): 67 if not hasattr(self, '_files'): 68 self._load_post_and_files() 69 return self._files 70 71 # ############### 看这里看这里 ############### 72 POST = property(_get_post, _set_post) 73 74 FILES = property(_get_files) 75 REQUEST = property(_get_request)
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,而【装饰器】方式针对经典类和新式类又有所不同
静态字段的成员修饰符:
python大致有两种修饰符:
1.公有(什么都没加比如xo,在类的方法中和类中都能访问到);
2.私有(只能在方法内部调用,内部访问、间接访问,不能在外部直接访问,在子类中也访问不了),私有的修饰符是两个下划线开头)
1 class Foo: 2 xo = 'xo' 3 __oox = 'oox' 4 def __init__(self): 5 pass 6 7 def fetch(self): 8 print(Foo.__oox) 9 10 def add(self, bk, rd): 11 pass 12 13 obj = Foo() 14 obj.fetch() # 用 print(obj.__oox) 和 print(Foo.__oox) 是访问不到__oox的,即不能通过对象调用访问
普通字段的修饰符:
1 class Foo: 2 def __init__(self): 3 self.__name = 'whisky' 4 print(self.__name) 5 6 def fetch(self): 7 print(self.__name) 8 9 def add(self, bk, rd): 10 pass 11 12 obj = Foo() 13 obj.fetch() 14 #输出: 15 #whisky 16 #whisky
普通方法的修饰符:
1 class Foo: 2 def __init__(self): 3 pass 4 5 def __fetch(self): 6 print('fetch method----') 7 8 def add(self): 9 self.__fetch() 10 11 obj = Foo() 12 obj.add() 13 #输出: 14 #fetch method----
静态方法的修饰符:
1 class Foo: 2 def __init__(self): 3 pass 4 5 def fetch(self): 6 Foo.__add() 7 8 @staticmethod 9 def __add(): 10 print('666') 11 12 @staticmethod 13 def dd(): 14 Foo.__add() 15 16 obj = Foo() 17 obj.fetch() 18 #输出:666 19 20 Foo.dd() 21 #输出:666
类方法和特性(不多)的修饰符和上面类似
小结:
对于每一个类的成员而言都有两种形式:
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能访问
私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
class C: def __init__(self): self.name = '公有字段' self.__foo = "私有字段"
私有成员和公有成员的访问限制不同:
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
1 class C: 2 3 name = "公有静态字段" 4 5 def func(self): 6 print C.name 7 8 class D(C): 9 10 def show(self): 11 print C.name 12 13 14 C.name # 类访问 15 16 obj = C() 17 obj.func() # 类内部可以访问 18 19 obj_son = D() 20 obj_son.show() # 派生类中可以访问
1 class C: 2 3 __name = "私有静态字段" 4 5 def func(self): 6 print(C.__name) 7 8 class D(C): 9 10 def show(self): 11 print(C.__name) 12 13 14 # C.__name # 类访问 ,无法访问 ==> 错误 15 16 obj = C() 17 obj.func() # 输出:私有静态字段 类内部可以访问 ==> 正确 18 19 obj_son = D() 20 #obj_son.show() # 派生类中 ,无法访问 ==> 错误 21 22 私有静态字段
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
ps:如果想要强制访问私有字段,可以通过 【对象._类名__私有字段明 】访问(如:obj._C__foo),不建议强制访问私有成员。
1 class C: 2 3 def __init__(self): 4 self.foo = "公有字段" 5 6 def func(self): 7 print(self.foo) # 类内部访问 8 9 class D(C): 10 11 def show(self): 12 print(self.foo) # 派生类中访问 13 14 obj = C() 15 16 obj.foo # 通过对象访问 17 obj.func() # 类内部访问 18 19 obj_son = D() 20 obj_son.show() # 派生类中访问
1 class C: 2 3 def __init__(self): 4 self.__foo = "私有字段" 5 6 def func(self): 7 print(self.__foo) # 类内部访问 8 9 10 class D(C): 11 12 def show(self): 13 print(self.__foo) # 派生类中访问 14 15 obj = C() 16 17 #obj.__foo # 通过对象访问 ==> 错误 18 obj.func() # 类内部访问 ==> 正确 19 20 obj_son = D() 21 #obj_son.show() # 派生类中访问 ==> 错误
方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
ps:非要访问私有属性的话,可以通过 对象._类__属性名
python类中的下划线、双下滑线:
_xxx 这表示这是一个保护成员(属性或者方法),它不能用from module import * 导入,其他方面和公有一样访问; __xxx 这表示这是一个私有成员,它无法直接像公有成员一样随便访问(比如直接print阿修改阿),当然,要想访问也可以,通过对象名._类名__xxx这样的方式可以访问; __xxx__ 这表示这是一个特殊成员,所谓特殊,就是例如__init__() __del__() __call__()这些niubi哄哄的特殊方法 xx: 公有变量_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问,使用 _Class__object可以访问
__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__xx_:单后置下划线,用于避免与Python关键词的冲突 (1)、以单下划线开头,表示这是一个保护成员,只有类对象和子类对象自己能访问到这些变量。以单下划线开头的变量和函数被默认当作是内部函数,使用from module improt *时不会被获取,但是使用import module可以获取
(2)、以单下划线结尾仅仅是为了区别该名称与关键词
(3)、双下划线开头,表示为私有成员,只允许类本身访问,子类也不行。在文本上被替换为_class__method
(4)、双下划线开头,双下划线结尾。一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突。是一些 Python 的“魔术”对象,表示这是一个特殊成员,例如:定义类的时候,若是添加__init__方法,那么在创建类的实例的时候,实例会自动调用这个方法,一般用来对实例的属性进行初使化,Python不建议将自己命名的方法写为这种形式。
(1)_xxx "单下划线 " 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,需通过类提供的接口进行访问;不能用'from module import *'导入 (2)__xxx 类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。)," 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
双下滑线保护级别更高、更严格
类的特殊成员__call__,读源码时会遇到
1 class Foo: 2 3 def __init__(self): 4 print('init') 5 6 def __call__(self, *args, **kwargs): 7 print('call') 8 return 1 9 10 r = Foo() 11 a = r() 12 print(a) 13 14 print('--------------------') 15 b = Foo()() # 类后面加两个()会执行类中的call方法。 16 print(b) 17 18 输出: 19 init 20 call 21 1 22 -------------------- 23 init 24 call 25 1
类的特殊成员__dict__ :类或对象中的所有成员
1 class Foo: 2 3 def __init__(self): 4 self.name = 'whisky' 5 6 def __getitem__(self, item): 7 print('__getitem__ : ',item, type(item)) 8 9 def __setitem__(self, key, value): 10 print('__setitem__ : ',key, value) 11 12 def __delitem__(self, key): 13 print('__delitem__ : ', key) 14 15 r = Foo() 16 a = r.__dict__ 17 print(a) # 输出 {'name': 'whisky'} 18 19 b = Foo.__dict__ 20 print(b) # 输出类中所有方法{'__init__': <function Foo.__init__ at 0x0000000000A53400>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__delitem__': <function Foo.__delitem__ at 0x0000000000A53598>, '__setitem__': <function Foo.__setitem__ at 0x0000000000A53510>, '__doc__': None, '__module__': '__main__', '__getitem__': <function Foo.__getitem__ at 0x0000000000A53488>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>}
# __setattr__ 、 __getattr__ 、__setitem__ 、 __getitem__ 区别: # https://www.cnblogs.com/yum777/p/7429253.html (1)__setattr__(self, item, value): 会拦截所有属性的的赋值语句,如果定义了这个方法,在给属性变量赋值时会调用__setattr__(self, item, value)方法,执行self.__dict__[key] = value。 当在__setattr__(self, item, value)方法内对属性进行赋值时,不可使用self.name = value,因为他会再次调用__setattr__(self, item, value)方法形成无限循环,最后导致堆栈溢出异常。 应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__[‘name’] = value (2)__setitem__(self, key, value): 同setattr方法类似,会拦截所有属性的赋值语句,区别在于如果将对象当作字典操作,设置键值对时会触发该方法,同样在__setitem__(self, key, value)方法内对属性进行赋值时, 也不能使用self.name = value,而应该使用self.__dict__[‘name’] = value. class Student(): def __setattr__(self, key, value): print('调用了setattr') self.__dict__[key] = value def __setitem__(self, key, value): print('调用了setitem') self.__dict__[key] = value s = Student() s.age = 1 # 调用__setattr__ 方法 s['name'] = 'tom' # 调用 __setitem__ 方法 __getattr__ : 如果某个类定义了__getattr__方法,同时系统在该类对象的实例字典中又找不到待查询的属性时,系统会自动调用__getattr__方法。 __getattribute__ : 程序每次访问对象的属性时,Python系统都会调用这个特殊的方法,即使属性字典里面已经有了该属性,也依然会触发__getattribute__方法。 __setattr__ : 设置类实例属性 如obj.key = 'tom',自调用__setattr__ __getitem__: 使用[]获取实例属性 如obj[key],自调用__getitem__; obj[key]的背后就是__getitem__方法,为了能求得obj[key]的值,解释器实际上会调用obj.__getitem__(key). __setitem__: 使用[]设置实例属性如 obj['key'] = 'tom' ,自调用__setitem__ class Student(object): def __getattr__(self, item): return item + " is not exists" def __setattr__(self, key, value): self.__dict__[key] = value def __getitem__(self, item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value if __name__ == '__main__': stu = Student() print(stu.name) # 调用__getattr__方法 stu.age = 22 # 调用__set__attr方法 print(stu["age"]) # 调用__getitem__方法 stu["name"] = "tom" # 调用__setitem__方法 print(stu["name"]) # 调用__getitem__方法 # >>执行结果 # name is not exists # 22 # tom
类的方法之特殊成员:__getitem__ , __setitem__ , __delitem__ 方法,自定义session框架时会用到
字典方法:
1 class Foo: 2 3 def __getitem__(self, item): 4 print(item) 5 6 def __setitem__(self, key, value): 7 print(key, value) 8 9 def __delitem__(self, key): 10 print(key) 11 12 r = Foo() 13 r['k1'] # []表示执行对象中的__getitem__方法 14 r['xxx'] = 123 # 有[]和等号时,会执行对象中的__setitem__方法 15 del r['the del key'] # del会执行类中的__delitem__方法 16 17 #输出 18 k1 19 xxx 123 20 the del key
列表方法:
1 class Foo: 2 3 def __getitem__(self, item): 4 print('__getitem__ : ',item, type(item)) 5 6 def __setitem__(self, key, value): 7 print('__setitem__ : ',key, value) 8 9 def __delitem__(self, key): 10 print('__delitem__ : ', key) 11 12 r = Foo() 13 r[1:3] # 通过切片或索引取值时,在python3中都执行__getitem__方法。(在python2中切片执行__getslice__方法) 14 r[1:3] = [11, 22, 33] # 执行__setitem__方法(在python2中执行__setslice__方法) 15 del r[1:3] # 执行__delitem__方法(在python2中执行__delslice__方法) 16 17 #输出 18 __getitem__ : slice(1, 3, None) <class 'slice'> 19 __setitem__ : slice(1, 3, None) [11, 22, 33] 20 __delitem__ : slice(1, 3, None)
类的方法之特殊成员:__iter__方法:
一个对象,如果可以被for循环迭代,说明对象中有__iter__这个方法,且方法中有对应的值.
1 class Foo: 2 3 def __iter__(self): 4 yield 1 5 yield 10 6 yield 100 7 8 9 obj = Foo() 10 #如果执行for一个对象时,自动会执行对象中的iter方法,如果对象中有yield,则为生成器,即迭代了一个生成器,for每循环一次,去取一次值 11 for i in obj: 12 print(i) 13 14 # 输出 15 1 16 10 17 100
异常:
异常处理时,如果不需要对异常分析,可以直接用Exception来捕捉所有错误,所有的错误类型都是exception的派生类(也叫子类、基类)
1 a = 'qwe' 2 try: 3 num = int(a) 4 print(num) 5 except IndexError as a: # 从上到下捕捉,此处捕捉不到向下继续判断 6 print(a, 'value error') 7 except Exception as e: # e是Exception类的一个对象,里面包含错误信息,Exception可以捕捉任何类型的错误 8 print(e, '-----------wrong')
异常其他结构
1 try: 2 # 主代码块 3 pass 4 except KeyError,e: #except可以有多个 5 # 异常时,执行该块 6 pass 7 else: 8 # 主代码块执行完,执行该块 9 pass 10 finally: 11 # 无论异常与否,最终执行该块 12 pass
1 你可以不带任何异常类型使用except,如下实例: 2 try: 3 正常的操作 4 ...................... 5 except: 6 发生异常,执行这块代码 7 ...................... 8 else: 9 如果没有异常执行这块代码 10 以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。
主动触发异常
1 try: 2 print(123) 3 raise Exception('错误了。。。') # 主动触发异常,并创建exception对象,接着赋值给e Exception内部应该有一个__str__方法 4 except Exception as e: # e是封装了错误信息的对象 5 print(e, '------------') 6 7 #输出 8 123 9 错误了。。。 ------------
python2和Python3异常的区别:
1 except Myerror, err: # In Python2 2 except Myerror as err: #In Python 3
特殊方法之类中的__str__
1 class Foo: 2 3 def __init__(self, arg): 4 self.xo = arg 5 6 def __str__(self): 7 return 'play str method ' 8 9 obj = Foo('whisky') 10 print(obj) 11 12 # 类中没有定义str方法时,输出<__main__.Foo object at 0x00000000006E74E0> 13 # 类中定义了str方法后, 输出 str方法返回的值,play str method
断言
1 # assert 条件 2 3 assert 1 == 1 #条件成立assert不做任何操作 4 5 assert 1 == 2 #条件不成立报错
补充:
类和对象在内存中是如何保存?
答:类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:

如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过 obj1 执行 【方法一】 时,过程如下:
- 根据当前对象中的 类对象指针 找到类中的方法
- 将对象 obj1 当作参数传给 方法的第一个参数 self
补充:
#静态方法: 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。 #类方法: 下面重点解释下classmethod怎么用 看下面的定义的一个时间类: class Data_test(object): day=0 month=0 year=0 def __init__(self,year=0,month=0,day=0): self.day=day self.month=month self.year=year def out_date(self): print "year :" print self.year print "month :" print self.month print "day :" print self.day t=Data_test(2016,8,1) t.out_date() 如果用户输入的是 “2016-8-1” 这样的字符格式,那么就需要调用Date_test 类前做一下处理: string_date='2016-8-1' year,month,day=map(int,string_date.split('-')) s=Data_test(year,month,day) 先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。 那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢? 那么@classmethod 就开始出场了 class Data_test2(object): day=0 month=0 year=0 def __init__(self,year=0,month=0,day=0): self.day=day self.month=month self.year=year @classmethod def get_date(cls,string_date): #这里第一个参数是cls, 表示调用当前的类名 year,month,day=map(int,string_date.split('-')) date1=cls(year,month,day) #返回的是一个初始化后的类 return date1 def out_date(self): print "year :" print self.year print "month :" print self.month print "day :" print self.day 在Date_test类里面创建一个成员函数, 前面用了@classmethod装饰。 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。 那么如何调用呢? r=Data_test2.get_date("2016-8-6") r.out_date() 输出: year : 2016 month : 8 day : 1 这样子等于先调用get_date()对字符串进行出来,然后才使用Data_test的构造函数初始化。 这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。 综上,classmethod 很大的作用在于初始化类时,数据结构不符合期望,可以添加额外的处理函数,然后返回一个初始化后的类。达到效果。
#静态方法: 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。 # 类方法: classmethod 很大的作用在于初始化类时,数据结构不符合期望,可以添加额外的处理函数,然后返回一个初始化后的类。达到效果。
当一个类init构造方法 需要 处理的逻辑非常多/非常复杂时, 简化构造方法逻辑,考虑使用classmethod,否则,构造方法里面要写很多逻辑, import tarfile tarfile的open方法可以参考
私有方法、析构方法、多态、多继承(mro查看继承顺序)、类方法、静态方法、__new__方法、__call__方法、类装饰器、自定义元类、引用计数、垃圾回收
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 在Python中可以通过在属性变量名前加上双下划线定义属性为私有属性 特殊变量命名 1、 _xx 以单下划线开头的表示的是protected类型的变量。即保护类型只能允许其本身与子类进行访问。若内部变量标示,如: 当使用“from M import”时,不会将以一个下划线开头的对象引入 。 2、 __xx 双下划线的表示的是私有类型的变量。只能允许这个类本身进行访问了,连子类也不可以用于命名一个类属性(类变量),调用时名字被改变(在类FooBar内部,__boo变成_FooBar__boo,如self._FooBar__boo) 3、 __xx__定义的是特列方法。用户控制的命名空间内的变量或是属性,如init , __import__或是file 。只有当文档有说明时使用,不要自己定义这类变量。 (就是说这些是python内部定义的变量名) 在这里强调说一下私有变量,python默认的成员函数和成员变量都是公开的,没有像其他类似语言的public,private等关键字修饰.但是可以在变量前面加上两个下划线"_",这样的话函数或变量就变成私有的.这是python的私有变量轧压(这个翻译好拗口),英文是(private name mangling.) **情况就是当变量被标记为私有后,在变量的前端插入类名,再类名前添加一个下划线"_",即形成了_ClassName__变量名.** Python内置类属性 __dict__ : 类的属性(包含一个字典,由类的数据属性组成) __doc__ :类的文档字符串 __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod) __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组) """ class pub(): _name = 'protected类型的变量' __info = '私有类型的变量' def _func(self): print("这是一个protected类型的方法") def __func2(self): print('这是一个私有类型的方法') def get(self): return(self.__info) a = pub() print(a._name) a._func() # print(a.info) # 执行结果: # protected类型的变量 # 这是一个protected类型的方法 # protected类型的变量和方法 在类的实例中可以获取和调用 # # print(a.__info) # # a.__func2() # 执行结果: # File "D:/Python/class/class3.py", line 46, in <module> # print(a.__info) # # AttributeError: pub instance has no attribute '__info' # a.__func2() # AttributeError: pub instance has no attribute '__func2' # 私有类型的变量和方法 在类的实例中获取和调用不到 # 获取私有类型的变量 print(a.get()) # 执行结果:私有类型的变量 # 如果想要在实例中获取到类的私有类形变量可以通过在类中声明普通方法,返回私有类形变量的方式获取 print(dir(a)) # 执行结果:['__doc__', '__module__', '_func', '_name', '_pub__func2', '_pub__info', 'get'] print(a.__dict__) # 执行结果:{} print(a.__doc__) # 执行结果: None print(a.__module__) # 执行结果:__main__ print(a.__bases__) # 执行结果: # print(a.__bases__) # AttributeError: pub instance has no attribute '__bases__'
# 私有方法(只会在类内部使用,实例化的对象用不上私有方法) # 使用场景:第三方短信接口: class Message: # 私有方法 def __send_msg(self): print('--------正在发送短信---------') # 公有方法: def send_msg(self, new_money): if new_money > 10000: self.__send_msg() else: print('余额不足,请先充值,再发送短信') obj = Message() obj.send_msg(1000000) # 小结:实际开发中如果一个类中的方法前面有两个下划线,表示这个方法比较重要,不想让它在外面直接被调用; # 而是间接在类中调用私有方法 # 注意:私有方法、私有属性都不会被继承(父代的小秘密不会告诉传递给子代- -!),但当私有属性、方法存在于父类的公有方法中时,则可以通过调用父类公有方法的方式来调用 # __del__方法: class Dog: def __del__(self): # 对象被回收之前调用 print('------Game Over-----') dog1 = Dog() dog2 = dog1 del dog1 del dog2 # 对象引用计数为0时,python自动执行类中的__del__方法 print('=================') # 多态:函数或类都可以当做变量传递 class Dog(object): def print_self(self): print('大家好,我是xxxx,多多关照') class Xiaotq(Dog): def print_self(self): print('hello everybody, i am the boss') def introduce(temp): # temp可能调用基类的方法,也有可能调用子类的方法,introduce执行的时候才能确定调的哪个方法 temp.print_self() dog1 = Dog() dog2 = Xiaotq() introduce(dog2) # 小结:定义一个函数的时候不知道调用的谁,可能存在多种调用情况,只有当程序执行的一刹那才知道调用谁 # 多继承:__mro__用法:多继承情况下可以查看类的对象搜索方法时的先后顺序 class base(object): def test(self): print('----base test----') class A(base): def test(self): print('----A test----') # 定义一个父类 class B(base): def test(self): print('----B test----') # 定义一个子类,继承自A、B class C(A,B): pass obj_C = C() obj_C.test() print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序 #----A test---- # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.base'>, <class 'object'>) # 类属性,实例属性 class People(object): country = 'china' def get(self): return People.country p = People() print(p.get()) p.country = 'asdasdda' p2 = People() print(p2.country) print(p.country) # 小结: # 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性, # (对其他实例化对象无影响),并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。 # 静态方法、类方法,区别: class Game(object): #类属性 num = 0 #实例方法 def __init__(self): #实例属性 self.name = "laowang" #类方法,属于类,通过类调用,和实例对象关系不大,无需传递self,必须传递cls @classmethod def add_num(cls): cls.num = 100 # 静态方法,可以通过类或实例对象调用,但和类、实例对象关系都不大,可以不传递任何参数(self,cls) @staticmethod def print_menu(): print("----------------------") print(" 穿越火线V11.1") print(" 1. 开始游戏") print(" 2. 结束游戏") print("----------------------") game = Game() #Game.add_num()#可以通过类的名字调用类方法 game.add_num()#还可以通过这个类创建出来的对象 去调用这个类方法 print(Game.num) #Game.print_menu()#通过类 去调用静态方法 game.print_menu()#通过实例对象 去调用静态方法 # 小结: # 开发中,类方法常用于修改类属性, 实例方法(__init__)常用于修改实例属性, 当需要在类中实现一个简单功能,和类、实例都没关系,这时候就用静态方法 # 类属性、类方法都是属于类的,尽量通过类去调用 # __new__ 方法的使用,python中规定了对象由类的__new__方法创建, 在类中重写__new__方法时,一定要返回父类的__new__方法才能完成对象创建功能,否则就无法创建对象 class Dog(object): def __init__(self): print("----init方法-----") def __del__(self): print("----del方法-----") def __str__(self): print("----str方法-----") return "对象的描述信息" def __new__(cls): # 重写父类的new方法,必须要返回父类的new方法完成对象的创建,cls此时是Dog指向的那个类对象 #print(id(cls)) print("----new方法-----") return object.__new__(cls) # 调用父类的__new__完成对象创建 #print(id(Dog)) xtq = Dog() # 经历的步骤: # 1.寻找并调用__new__方法创建对象,找一个变量接收__new__的返回值,返回值表示创建出来的对象的引用 # 2 __init__(self),self表示刚刚创建出来的对象的引用 # 小结: # __new__方法:创建,专门用来实例化创建对象,每一个对象被创建实例化都是通过类中的__new__方法 # 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 # __new__()方法:必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。 # __init__()有一个参数self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作 # __new__方法的应用:创建单例模式 # class Dog(object): # # __instance = None # # def __new__(cls): # if cls.__instance == None: # cls.__instance = object.__new__(cls) # return cls.__instance # else: # #return 上一次创建的对象的引用 # return cls.__instance # # class Poo(Dog): # # def test(self): # print('123') # # a = Poo() # print(id(a)) # b = Poo() # print(id(b)) # __call__方法的使用: class Test(object): def __call__(self, *args, **kwargs): # 有__call__的类对象可以直接被调用 print('-----test-----') t = Test() t() # ----------类装饰器-------- class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s" % func.__name__) # func.__name__ 表示函数名 self.__func = func def __call__(self): print("---装饰器中的功能---") self.__func() @Test # 相当于 test = Test(test), 执行test()相当于执行类装饰器中的__call__方法 def test(): print("----test---") test() # 相当于调用Test(test)(),即执行类装饰器的call方法 # __metaclass__属性 # 你可以在定义一个类的时候为其添加__metaclass__属性。 # class Foo(object): # __metaclass__ = something… # ...省略... # 如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下class Foo(object),但是类Foo还没有在内存中创建。 # Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。把下面这段话反复读几次。当你写如下代码时 : # class Foo(Bar): # pass # Python做了如下的操作: # # 1、Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象) # 2、如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。 # 3、如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。 # 4、如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。 # 现在的问题就是,你可以在__metaclass__中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。 # 自定义元类 # 元类的主要目的就是为了当创建类时能够自动地改变类。通常,你会为API做这样的事情,你希望可以创建符合当前上下文的类。 # 假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。 # 幸运的是,__metaclass__实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。 # python2中:__metaclass__ #-*- coding:utf-8 -*- def upper_attr(future_class_name, future_class_parents, future_class_attr): #遍历属性字典,把不是__开头的属性名字变为大写 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value #调用type来创建一个类 return type(future_class_name, future_class_parents, newAttr) class Foo(object): __metaclass__ = upper_attr #设置Foo类的元类为upper_attr bar = 'bip' print(hasattr(Foo, 'bar')) print(hasattr(Foo, 'BAR')) f = Foo() print(f.BAR) # python3中 :metaclass代替__metaclass__ #-*- coding:utf-8 -*- def upper_attr(future_class_name, future_class_parents, future_class_attr): #遍历属性字典,把不是__开头的属性名字变为大写 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value #调用type来创建一个类 return type(future_class_name, future_class_parents, newAttr) class Foo(object, metaclass=upper_attr): bar = 'bip' print(hasattr(Foo, 'bar')) print(hasattr(Foo, 'BAR')) f = Foo() print(f.BAR) # 现在让我们再做一次,这一次用一个真正的class来当做元类。 #coding=utf-8 class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而__init__只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 def __new__(cls, future_class_name, future_class_parents, future_class_attr): #遍历属性字典,把不是__开头的属性名字变为大写 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value # 方法1:通过'type'来做类对象的创建 # return type(future_class_name, future_class_parents, newAttr) # 方法2:复用type.__new__方法 # 这就是基本的OOP编程,没什么魔法 # return type.__new__(cls, future_class_name, future_class_parents, newAttr) # 方法3:使用super方法 return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr) #python2的用法 class Foo(object): __metaclass__ = UpperAttrMetaClass bar = 'bip' # python3的用法 # class Foo(object, metaclass = UpperAttrMetaClass): # bar = 'bip' print(hasattr(Foo, 'bar')) # 输出: False print(hasattr(Foo, 'BAR')) # 输出:True f = Foo() print(f.BAR) # 输出:'bip' # # 就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身而言,它们其实是很简单的: # # 拦截类的创建 # 修改类 # 返回修改之后的类 # 究竟为什么要使用元类? # # 现在回到我们的大主题上来,究竟是为什么你会去使用这样一种容易出错且晦涩的特性?好吧,一般来说,你根本就用不上它: # # “元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters # python垃圾回收: # ⼩整数[-5,257)共⽤对象,常驻内存 # 单个字符共⽤对象,常驻内存 # 单个单词,不可修改,默认开启intern机制,共⽤对象,引⽤计数为0,则销毁 # GC系统所承担的⼯作远⽐"垃圾回收"多得多。实际上,它们负责三个重要任务。它们为新⽣成的对象分配内存,识别那些垃圾对象,并且 # 从垃圾对象那回收内存 # 引用计数机制: # python⾥每⼀个东⻄都是对象,当⼀个对象有新的引⽤时,它的计数就会增加,当引⽤它的对象被删除,它的引用计数就会减少, # 当引⽤计数为0时,该对象⽣命就结束了 # 循环引用: # list1 = [] # list2 = [] # list1.append(list2) # list2.append(list1) # list1与list2相互引⽤,如果不存在其他对象对它们的引⽤,list1与list2的引⽤计数也仍然为1,所占⽤的内存永远⽆法被回收, # 这将是致命的。 对于如今# 的强⼤硬件,缺点1尚可接受,但是循环引⽤导致内存泄露,注定python还将引⼊新的回收机制。(分代收集) # 引用计数为主,隔代回收为辅(解决引用计数的循环引用问题),完成所有对象的垃圾回收
单例模式:
1 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。 2 3 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。 4 5 在 Python 中,我们可以用多种方法来实现单例模式
使用模块
1 # 其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做: 2 3 # mysingleton.py 4 5 class Singleton(object): 6 def foo(self): 7 pass 8 singleton = Singleton() 9 10 # 将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象 11 from a import singleton
使用类的方式实现:
1 class Singleton(object): 2 3 def __init__(self): 4 pass 5 6 @classmethod 7 def instance(cls, *args, **kwargs): 8 if not hasattr(Singleton, "_instance"): 9 Singleton._instance = Singleton(*args, **kwargs) 10 return Singleton._instance
1 import threading 2 import time 3 4 class Singleton(object): 5 _instance_lock = threading.Lock() 6 7 def __init__(self, *args, **kwargs): 8 print('second') 9 # time.sleep(1) 10 11 @classmethod 12 def instance(cls, *args, **kwargs): 13 print('first') 14 if not hasattr(Singleton, '_instance'): 15 with Singleton._instance_lock: # 加锁:同一时间只能有一个线程获得锁(Singleton._instance = Singleton(*args, **kwargs)进而执行类实例化暂存如私有属性中),构造方法只调用一次;第二个线程过来后直接返回上次构建好的对象 16 if not hasattr(Singleton, "_instance"): 17 # time.sleep(0.0005) # 多线程阻塞设置一个时间,CPU时间片轮换到下个线程,下一行代码赋值还没来得及执行,下一个线程又进入if条件... 18 Singleton._instance = Singleton(*args, **kwargs) 19 return Singleton._instance 20 21 22 def test(i): 23 obj = Singleton.instance() # 如果上面没有加锁,多个线程同时读取类中的数据进行实例化到各自线程的寄存器中,没有阻塞拿到的对象都是同一个 24 print(id(obj),obj,'--thread %s' % i ) 25 26 if __name__ == '__main__': 27 for i in range(10): 28 p = threading.Thread(target=test,args=(i,)) 29 p.start() 30 31 # 这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 32 # obj = Singleton.instance() 33 34 # 如果用 35 # obj = Singleton(), 这种方式得到的不是单例
1 可能有延迟加载或者缓存的原因,只有一次判断,仍然不能保证系统是否只创建了一个单例,也可能出现多个实例的情况 加锁之前要检测,加锁之后要检测 2 3 import time 4 import threading 5 class Singleton(object): 6 _instance_lock = threading.Lock() 7 8 def __init__(self): 9 time.sleep(1) 10 11 @classmethod 12 def instance(cls, *args, **kwargs): 13 with Singleton._instance_lock: 14 if not hasattr(Singleton, "_instance"): 15 Singleton._instance = Singleton(*args, **kwargs) 16 return Singleton._instance 17 18 19 def task(arg): 20 obj = Singleton.instance() 21 print(obj) 22 for i in range(10): 23 t = threading.Thread(target=task,args=[i,]) 24 t.start() 25 time.sleep(20) 26 obj = Singleton.instance() 27 print(obj) 28 29 还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行: 30 @classmethod 31 def instance(cls, *args, **kwargs): 32 if not hasattr(Singleton, "_instance"): 33 with Singleton._instance_lock: 34 if not hasattr(Singleton, "_instance"): 35 Singleton._instance = Singleton(*args, **kwargs) 36 return Singleton._instance 37 38 这样,一个可以支持多线程的单例模式就完成了 39 40 完整代码: 41 import time 42 import threading 43 class Singleton(object): 44 _instance_lock = threading.Lock() 45 46 def __init__(self): 47 time.sleep(1) 48 49 @classmethod 50 def instance(cls, *args, **kwargs): 51 if not hasattr(Singleton, "_instance"): 52 with Singleton._instance_lock: 53 if not hasattr(Singleton, "_instance"): 54 Singleton._instance = Singleton(*args, **kwargs) 55 return Singleton._instance 56 57 58 def task(arg): 59 obj = Singleton.instance() 60 print(obj) 61 for i in range(10): 62 t = threading.Thread(target=task,args=[i,]) 63 t.start() 64 time.sleep(20) 65 obj = Singleton.instance() 66 print(obj)
基于__new__方法实现的单例模式,推荐:
1 import time 2 class Singleton(object): 3 4 def __new__(cls, *args, **kwargs): 5 time.sleep(1) 6 if not hasattr(Singleton, "_instance"): 7 time.sleep(2) 8 Singleton._instance = object.__new__(cls) 9 return Singleton._instance 10 11 12 obj1 = Singleton() 13 obj2 = Singleton() 14 obj3 = Singleton() 15 print(id(obj1)) 16 print(id(obj2)) 17 print(id(obj3)) 18 print(obj1)
1 import time 2 import threading 3 class Singleton(object): 4 _instance_lock = threading.Lock() 5 6 def __init__(self): 7 pass 8 9 10 def __new__(cls, *args, **kwargs): 11 if not hasattr(Singleton, '_instance'): 12 with Singleton._instance_lock: 13 if not hasattr(Singleton, "_instance"): 14 Singleton._instance = object.__new__(cls) 15 return Singleton._instance 16 17 18 def task(arg): 19 obj = Singleton() 20 print(id(obj)) 21 22 23 if __name__ == '__main__': 24 for i in range(10): 25 t = threading.Thread(target=task,args=[i,]) 26 t.start() 27 28 # 采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()
1 import threading 2 import time 3 4 5 class Dog(object): 6 __instance_lock = threading.Lock() 7 __instance = None 8 9 def __new__(cls): 10 with Dog.__instance_lock: 11 time.sleep(1) 12 if not cls.__instance: 13 cls.__instance = object.__new__(cls) 14 return cls.__instance 15 return cls.__instance 16 17 18 class Poo(Dog): 19 20 def test(self): 21 print('123') 22 23 def test(i): 24 obj = Dog() 25 print('thread %s ' % i,id(obj)) 26 27 if __name__ == '__main__': 28 for i in range(10): 29 t = threading.Thread(target=test, args=(i,)) 30 t.start()
基于元类实现单例模式:
1 # 基于metaclass方式实现,通过元类的__call__方法实现: 2 import threading 3 4 class SingletonType(type): 5 _instance_lock = threading.Lock() 6 def __call__(cls, *args, **kwargs): 7 if not hasattr(cls, "_instance"): 8 with SingletonType._instance_lock: 9 if not hasattr(cls, "_instance"): # 如果子类没有单例对象属性,通过元类创建 10 cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) 11 return cls._instance 12 13 class Foo(metaclass=SingletonType): 14 def __init__(self,name): 15 self.name = name 16 17 18 obj1 = Foo('name') 19 obj2 = Foo('name') 20 print(obj1,obj2)
基于装饰器:
1 def Singleton(cls): 2 _instance = {} 3 4 def _singleton(*args, **kargs): 5 if cls not in _instance: 6 _instance[cls] = cls(*args, **kargs) 7 return _instance[cls] 8 9 return _singleton 10 11 12 @Singleton 13 class A(object): 14 a = 1 15 16 def __init__(self, x=0): 17 self.x = x 18 19 20 a1 = A(2) 21 a2 = A(3)
注意:
除了模块单例外,其他几种模式的本质都是通过设置中间变量,来判断类是否已经被实例。区别就是中间变量的位置不同,或设置在元类中,或封装在函数中,或设置在类中作为静态变量。
注意1:中间变量的访问和更改存在线程安全的问题:在开启多线程模式的时候需要加锁处理。
注意2:__new__方法无法避免触发__init__(),初始的成员变量会进行覆盖。基于装饰器的方法不会。https://www.cnblogs.com/yxi-liu/p/singleton.html

单例模式的优缺点和使用场景
不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

https://www.cnblogs.com/restartyang/articles/7770856.html

浙公网安备 33010602011771号