12 new方法和单例、定制访问函数、装饰器
-
上节课作业解答
# 通过多重继承方法,分别定义出动物,人类,和和荷兰人三种类 class Animal(object): def __init__(self, name): self.name = name def eat(self): print('%s正在吃东西' % self.name) def breath(self): print('%s正在呼吸' % self.name) class Person(Animal): def __init__(self, name, money): super().__init__(name) self.money = money def speak(self): print('%s说他有%s人民币' % (self.name, self.money)) class Fulan(Person): def eat(self): print('弗兰人%s不仅爱吃辣,而且爱吃槟榔' % self.name) def speak(self): print('福兰人%s正在blablabla讲弗兰话。' % self.name) juhao = Fulan('句号', 23) juhao.breath() juhao.eat() juhao.speak() #拼接一个Spiderman class Spider(Animal): def climb(self): print('%s正在攀岩' % self.name) def tusi(self): print('%s正在吐丝' % self.name) class Spiderman(Person, Spider): pass Spiderman = Spiderman('Spiderman', 10) Spiderman.tusi() Spiderman.climb() Spiderman.eat() Spiderman.breath() #说明: 1. 调用父类的两种方法: Animal.__init__(self.name) super().__init__(name) 2. 不能直接调用__init__,且__init__只能有一个 以上运行结果 句号正在呼吸 弗兰人句号不仅爱吃辣,而且爱吃槟榔 福兰人句号正在blablabla讲弗兰话。 Spiderman正在吐丝 Spiderman正在攀岩 Spiderman正在吃东西 Spiderman正在呼吸
一、魔术方法补充
所有的函数都是类(对象)
魔术方法示例:(__方法__)
(一) 引入
a = 'hahaha' b = '12' print(b + a) # 等效于 print(b.__add__(a)) # 运行结果 # 12hahaha # 12hahaha
(二) 魔术方法在类中的使用
-
示例
__add__
# 添加其他属性
# 魔术方法在类中的使用 class Rectangle(object): def __init__(self, length, width): self.length = length self.width = width def get_area(self): return self.length * self.width def __add__(self, other): add_length = self.length + other.length add_width = self.width + other.width return add_length, add_width
-
示例
__str__
__repr__
str(面向使用者,提供简洁有用信息)
repr(面向开发者,提供接近创建时的信息,让开发者可以通过复制粘贴来重建对象)
直接在交互界面中,直接返回值和print()所使用的(str和repr)魔术方法不同
# str和repr原理 # 例 1 def __str__(self): return 'length is %s, width is %s' % (self.length, self.width) def __repr__(self): return '长是%s,宽是%s'% (self.length, self.width) def __call__(self, *args, **kwargs): print('我正在被调用') rec1 = Rectangle(10, 20) rec2 = Rectangle(30, 40) rec1() print(rec1 + rec2) print(str(rec1)) # (如果不写rec1会返回函数体<……>) print(repr(rec1)) # (如果不写rec1会返回函数体<……>) # 运行结果 我正在被调用 (40, 60) length is 10, width is 20 长是10,宽是20 # 注: 直接在交互界面中,直接返回值和print()使用的(str和repr)魔术方法不同 # 例 2 a = '1,2,3' a '1,2,3' print(a) 1,2,3 # 说明: 函数可以直接调用,但是类必须要使用call魔术方法才可以直接调用
(三) 简单魔术方法(了解)
__class__ | 查看类名 |
---|---|
__base__ |
查看继承的父类 |
__mro__ |
追根溯源到object,super是基于mro实现的 |
__bases__ |
查看继承的全部父类 |
__dict__ |
查看全部属性,返回属性和属性值键值对形式 |
__doc__ |
查看对象文档,即类中的注释(用引号注释的部分)(注释不能被继承) |
__dir__ |
查看全部属性和方法(等效于dir) |
二、new方法和单例
在初始化函数init之前执行,创建实例
# __new__ a = Rectangle.__new__() Rectangle.__init__(a) # 单例模式(节省内存) class Esrth(object): def __new__(cls, *args, **kwargs): if not hasattr(cls,'instance'): cls.instance = super().__new__(cls) return cls.instance def __init__(self): self.name = '单例' a = Esrth() print(id(a)) b = Esrth() print(id(b)) # 把e覆盖了 #运行结果 3072614988 3072614988 # 说明: 1. 在以上例子中可以看见两个实例的id其实是相同的,意味着这两个实例其实引用的是同一个实例,(只占一个内存)是一个实例的不同名字 2. 其中self代表实例本身,cls代表类本身hasattr判断类中有没有('')参数 3. 如果没有就调用object中new方法
(一)__new__方法: class Hero(object): def __init__(self): print('这是init方法') def __new__(cls): print('这个cls是:%s' % cls) print('这是new方法') return object.__new__(cls) garen = Hero() 运行结果: 这个cls是:<class '__main__.Hero'> 这是new方法 这是init方法 说明: 四个点理解__new__方法 1、__new__方法是在 类 创建实例 的时候自动调用的。 2、实例是通过类里面的__new__方法是在 类 创建出来的。 3、先调用__new__方法创建实例,再调用 __init__方法初始化实例。 4、__new__方法,,后面括号里的cls代表的是类本身 在上面的例子中,我们可以看到创建实例的时候,自动调用了__new__,方法和__init__方法,并且是先调用的__new__再调用的__init__方法,打印 cls 的时候显示的这个Hero类
(二)单例模式:
class Hero(object): __instance = None # 定义一个私有属性__instance等于None def __new__(cls): if cls.__instance == None: # 然后我们判断它是不是等于None,如果等于None,我们调用父类的方法创建一个实例对象,并把返回的对象赋值给 __instance,并且返回__instance cls.__instance = object.__new__(cls) return cls.__instance else: # 如果__instance不等于None,那就说明已经创建了对象我们直接把__instance返回去。 return cls.__instance a = Hero() b = Hero() print(id(a)) print(id(b)) 打印结果: 3072101708 3072101708 # 说明: 单例模式实现的原理:通过重写__new__方法,让__new__只能进行一次实例创建。 在上面的例子中,我们可以看到两个实例的ID是相同的,意味着第二次创建的时候,并没有真正的去创建,而是引用的第一次创的实例,同一个实例的不同名字
三、定制访问函数
(一) 常见方法
作用 | 方法 | 返回 |
---|---|---|
查 | hasattr(re,'length') |
返回bool值 |
getattr(re,'length') |
返回属性值 | |
b.__getattribute__('length') |
返回全部属性值 | |
改 | setattr(b,'length',6) |
|
b.__setattr__('length',5) |
||
增 | b.aaa = 1 |
|
setattr(b,'leg',2) |
||
b.__setattr__('leg',3) |
||
删 | delattr(b,'leg') |
|
b.__delattr__('leg') |
||
del b |
# 实例: class Rectangle(object): def __init__(self,length,width): self.length = length self.width = width def area(self): return self.length * self.width a = Rectangle(10,20) print(a.area()) # 查 print(hasattr(a,'length')) # 返回bool值 print(getattr(a,'length')) # 返回属性值 print(a.__getattribute__('width')) # 返回属性值 # 改 setattr(a,'length',5) a.__setattr__('width',5) print(a.length) print(a.width) # 增 a.aaa = 1 setattr(a,'bbb',2) a.__setattr__('ccc',3) print(a.aaa) print(a.bbb) print(a.ccc) # 删 delattr(a,'ccc') a.__delattr__('bbb') del a # print(a.ccc) # print(a.bbb) # print(a.length) 打印结果: 200 True 10 20 5 5 1 2 3 注:print(hasattr(实例rec1,属性'length'))等效于print(rec1.__hasattr__('name'))
(二) 举例
# 定制属性访问 # 例 __getattr__ re = Rectangle(3,4) def __getattr__(self,item): print('no attribute') # 当属性不存在时,若定义了此方法则调用此方法,若存在则返回原值
(三) 了解__getattribute__
b.__getattribute__('length') # 正常调用返回属性值,属性值不存在,调用__getattr__未定义时会报错
四、装饰器
(一) 引入
def eat(): return '小明在吃东西' def boom(): return eat() + '并且爆炸了' print(boom()) #等效于 def boom(func): return func() + '并且爆炸了' xiaoming = boom(eat) print(xiaoming) # 运行结果 小明在吃东西并且爆炸了 小明在吃东西并且爆炸了
(二) 改善
可以看到输出时方式不一样,可以修改为以下操作
# 例 1 def boom(func): def new_func(): return func() + '并且爆炸了' return new_func xiaoming = boom(eat) print(xiaoming()) #运行结果 小明在吃东西并且爆炸了
# 例 2 def eat2(name): return '小明和%s在吃东西' % name def boom2(func2): def new_func2(name): return func2(name) + '并且爆炸了' return new_func2 eat2 = boom2(eat2) print(eat2('小红')) #运行结果 小明和小红在吃东西并且爆炸了
(三) 装饰器
例1 def boom3(func3): def new_func3(name): return func3(name) + '并且爆炸了' return new_func3 @boom3 def eat3(name): return '小明和%s在吃东西' % name print(eat3('小红')) # 运行结果 小明和小红在吃东西并且爆炸了
例2 def f1(fu): def func2(): print('这是装饰器功能代码') fu() return func2 @f1 def func(): print('这是基础功能代码') func = f1(func) # 等同@f1 func() 打印结果: 这是装饰器功能代码 这是装饰器功能代码 这是基础功能代码
(四) 其他装饰器
class Rectangle: def __init__(self, length, width): self.length = length self.width = width def area(self): areas = self.length * self.width return areas # 像访问属性一样 @property def areal(self): return self.width *self.length #静态方法 @staticmethod def func(): #如果写了self在调用时会报错 print('staticmethod func') #类方法 @classmethod def show(cls): #cls代表类本身 如果加上self在调用时要把实例传入 print(cls) print('show fun') # 用类做装饰器 class TestClass(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print('1234') return self.func() @TestClass def shuzi(): print('5678') shuzi() # 运行结果 1234 5678
(五) 装饰器参考
from datetime import datetime def run_time(func): def new_func(*args,**kwargs): start_time = datetime.now() print('程序开始时间:%s' % start_time) func(*args, **kwargs) end_time = datetime.now() print('程序结束时间:%s' % end_time) print('程序总共执行时间:%s' % (end_time - start_time)) return new_func() print(datetime.now()) # 运行结果 2018-04-13 17:10:39.332348