Python面向对象之多态、鸭子类型、魔法方法、内置方法、上下文管理器
一、多态
1、即同一个事物的多种形态
如动物:狗、猪
class Animal: pass class Dog(Animal): pass class Pig(Animal): pass
2、多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
# class Animal: # 统一所有子类的方法 # def say(self): # print('动物基本的发声模拟。。。',end=' ') # class Dog(Animal): # def say(self): # super().say() # print('汪汪汪') # # class Pig(Animal): # def say(self): # super().say() # print('哼哼哼') # obj1=Dog() # obj3=Pig() # obj1.say() # obj3.say() # 定义统一的接口,接收传入的动物对象 # def animal_say(animal): # animal.say() # animal_say(obj1) # animal_say(obj3)
3、鸭子类型
在Python中推崇的是鸭子类型
在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由“当前方法和属性的集合”决定
class Duck(): def walk(self): print('I walk like a duck') def swim(self): print('i swim like a duck') class Person(): def walk(self): print('this one walk like a duck') def swim(self): print('this man swim like a duck') '''Person类拥有跟Duck类一样的方法,当有一个函数调用Duck类,并利用到了两个方法walk()和swim()。我们传入Person类也一样可以运行,函数并不会检查对象的类型是不是Duck,只要他拥有walk()和swim()方法,就可以正确的被调用。'''
二、oop相关内置函数
1、isinstance(obj,cls):判断一个对象是否是某个类的实例(判断类型的方法)
# 示例程序: class Foo(object): pass obj = Foo() print(isinstance(obj, Foo)) #True
2、issubclass(sub,super):判断一个类是否是另一个类的子类
class F(object): pass class Bar(F): pass print(issubclass(Bar, F))
三、类中的魔法方法
1、__str__:调用str函数或者print函数时自动执行,返回值作为显示内容。可以利用该函数来自定义,对象的是打印格式。如果`__str__`没有被定义,那么就会使用`__repr__`来代替输出
这俩方法的返回值必须是字符串,否则抛出异常。
class Person: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return "这是一个person对象 name:%s age:%s" %(self.name,self.age) pass # p=Person("aaaa",19) print(p) #这是一个person对象 name:aaaa age:19
2、__del__ 当手动删除对象时立马执行,或是程序运行结束时也会自动执行。当对象在使用过程中,打开了不属于解释器额资源,例如:文件 网络端口(网络编程)就可使用
class FileTool: """该类用于简化文件的读写操作 """ def __init__(self,path): self.file = open(path,"rt",encoding="utf-8") self.a = 10 def read(self): return self.file.read() # 如果确定程序执行完。这个对象肯定不使用了 可以放心的关闭问文件 def __del__(self): self.file.close() tool = FileTool("a.txt") print(tool.read())
3、__call__ 执行时机:在调用对象时自动执行(即对象+括号)对象=类名()
class A: def __call__(self, *args, **kwargs): print("call run") print(args) print(kwargs) a = A() a(4,a="私我") ''' call run (4,) {'a': '私我'} '''
4、__slots__ :该属性是一个类属性,用于优化对象内存占用
字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示,实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。 在__slots__中列出的属性名在内部被映射到这个数组的指定下标上。 使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
class Foo: __slots__=['name','age'] f1=Foo() f1.name='aaa' f1.age=18 print(f1.__slots__) #['name', 'age'] #f1.y=2 报错 #print(f1.__dict__) #f1不再有__dict__ ,这里报错,归__slots__管,节省内存
5、__format__:调用format函数时自动执行,用于定制对象的格式化输出,
#规定输出格式 date_dic={ 'ymd':'{0.year}:{0.month}:{0.day}', 'dmy':'{0.day}/{0.month}/{0.year}', 'mdy':'{0.month}-{0.day}-{0.year}', } class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day def __format__(self, format_spec): if not format_spec or format_spec not in date_dic: format_spec='ymd' fmt=date_dic[format_spec] return fmt.format(self) d1=Date(2016,12,29) print(format(d1)) #2016:12:29 print('{:mdy}'.format(d1)) #12-29-2016
四、属性的getattr setattr 和delattr getattribute
#点语法.操作对象属性时自动触发 getattr:使用点语法访问属性的时候如果属性不存在时才执行 setattr:用点语法设置属性(修改/添加)时触发它的执行 delattr:使用点语法删除属性时触发
getattribute: 该函数也是用来获取属性的:在获取属性时如果存在gqtattribute 则先执行该函数,如果没有拿到属性则继续调用getattr函数,如果拿到了则直接返回
class A: def __setattr__(self, key, value):# 设置 # print(key) # print(value) print("__setattr__") self.__dict__[key] = value def __delattr__(self, item): # 删除 print("__delattr__") print(item) self.__dict__.pop(item) pass def __getattr__(self, item): # 获取属性 print("__getattr__") return 1 def __getattribute__(self, item): print("__getattribute__") # return self.__dict__[item] return super().__getattribute__(item) a = A() a.name = "cc" # print(a.name) ''' __setattr__ __getattribute__ __getattribute__ jack ''' del a.name ''' __setattr__ __getattribute__ __delattr__ name __getattribute__ '''
五、[ ] 的原理:
getitem 当你用中括号去获取属性时 执行
setitem 当你用中括号去设置属性时 执行
delitem 当你用中括号去删除属性时 执行
class A: def __getitem__(self, item): print("__getitem__") return self.__dict__[item] def __setitem__(self, key, value): print("__setitem__") self.__dict__[key] = value def __delitem__(self, key): del self.__dict__[key] print("__delitem__") a = A() # a.name = "cc" a["name"] = "cc" print(a["name"]) del a["name"] ''' __setitem__ __getitem__ cc __delitem__ '''
六、运算符重载:对象比较大小
原本自定义对象无法直接使用大于、小于来进行比较,我们可自定义运算符来实现,让自定义对象也支持比较云算符。__gt__:大于比较、 def __It__:小于比较、 def__eq__:等于比较
class Student(object): def __init__(self,name,age,height): self.name=name self.age=age self.height=height def __gt__(self, other): # 比较大于 # print(self) print(other) return "有点大" def __eq__(self, other): # 比较等于 #print(self) print(other) if self.name == other.name and self.age == other.age: return "姓名和年龄一样" else: return "姓名和年龄不一样" def __lt__(self, other): # 比较小于 if self.age<other: return "太小了" s=Student("reso",20,185) s1=Student("Jack",18,155) # print(s.name,s1.age) print(s>s1) #有点大 #print(sprint(s==s1) #姓名和年龄不一样
七、迭代器协议
迭代器是指具__iter__和__next__的对象,为对象增加这两个方法来让对象变成一个迭代器
class MyRange: def __init__(self, start, end, step): self.start = start self.end = end self.step = step def __iter__(self): return self def __next__(self): a = self.start self.start += self.step if a < self.end: return a else: return "停止"for i in MyRange(1, 5, 1): print(i) ''' 1 2 3 4 '''
八、上下文管理
上下文可以理解为是一个代码区间,一个范围,例如 witho open 打开的文件仅在这个上下文中有效涉及的方法:
enter: 表示进入上下文 exit : 表示退出上下文
当执行with语句时 会先执行enter,当代码执行完毕后执行exit 或者代码遇到了异常会立即执行exit,并传入错误的信息。包含:错误的类型 错误的信息 错误的追踪信息
__enter__
出现with
语句,对象的__enter__
被触发,有返回值则赋值给as声明的变量
__exit__
with中代码块执行完毕时执行
内存泄漏:内存泄露的根本原因在于创建了某个对象,却没有及时的释放掉,直到程序结束前,这个未被释放的对象一直占着内存。如果所占内存量少就影响不大,如果量大那么就会直接把内存占满,导致程序被 kill 掉,这就是内存泄露。文件的输入输出、数据库的连接断开等,都是很常见的资源管理操作。但资源都是有限的,在写程序时,我们必须保证这些资源在使用过后得到释放,不然就容易造成资源泄露,轻者使得系统处理缓慢,重则会使系统崩溃。在 Python 中,对应的解决方式便是上下文管理器(context manager)。
基于类的上下文管理器:
class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def __enter__(self): # print('enter') self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): # print('exit') self.f.close() return True def __getattr__(self, item): return getattr(self.f,item) with Open('test.txt','w') as f: print(f) f.write('aaaaaabbbbbccccc') #f.quit #否则抛出异常,交给__exit__处理
'''
执行顺序
# 当我们用 with 语句,执行这个上下文管理器时: # 1. 方法`__init__()`被调用,程序初始化对象 open, # 2. 方法`__enter__()`被调用,文件被打开,并且返回 open 对象赋予变量 f; # 3. 字符串“aaaaaabbbbbccccc”被写入文件“test.txt”;
# 4. 方法`__exit__()`被调用,负责关闭之前打开的文件流。
注意:另外,__exit__()
方法中的参数 “exc_type, exc_val, exc_tb”,分别表示 exception_type、exception_value 和 traceback。当我们执行含有上下文管理器的 with 语句时,如果有异常抛出,异常的信息就会包含在这三个变量中,传入方法__exit__()
。
对于__exit__;
class Foo: def __init__(self): print('__init__ called') def __enter__(self): print('__enter__ called') return self # 在__exit__方法中捕获并输出异常信息 def __exit__(self, exc_type, exc_value, exc_tb): print('__exit__ called') if exc_type: print(f'exc_type: {exc_type}') print(f'exc_value: {exc_value}') print(f'exc_traceback: {exc_tb}') print('exception handled') return True # 异常处理后必须返回True # 调用并手动抛出异常 with Foo() as obj: raise Exception('exception raised').with_traceback(None) # 输出 # __init__ called # __enter__ called # __exit__ called # exc_type:# exc_value: exception raised # exc_traceback: # exception handled
上下文管理器对数据库操作
class DBCM: # 负责对数据库进行初始化,也就是将主机名、接口(这里是 localhost 和 8080)分别赋予变量 hostname 和 port; def __init__(self, hostname, port): self.hostname = hostname self.port = port self.connection = None # 连接数据库,并且返回对象; def __enter__(self): self.connection = DBClient(self.hostname, self.port) return self # 负责关闭数据库的连接 def __exit__(self, exc_type, exc_val, exc_tb): self.connection.close() with DBCM('localhost', '8080') as db_client: 代码...
基于生成器的上下文管理器
使用装饰器 contextlib.contextmanager,来自定义基于生成器的上下文管理器,用以支持 with 语句。
from contextlib import contextmanager @contextmanager def file_manager(name, mode): try: f = open(name, mode) yield f finally: f.close() with file_manager('test.txt', 'w') as f: f.write('hello world') '''函数 file_manager() 是一个生成器,当我们执行 with 语句时,便会打开文件,并返回文件对象 f; 当 with 语句执行完后,finally block 中的关闭文件操作便会执行。 注意:基于生成器定义的上下文管理需要使用装饰器 @contextmanager,不再使用生成器协议方法。'''