Python内置方法与面向对象知识点进阶系列
Python中类的魔法方法与内置方法汇总
之前总结的一些双下划线方法
常见的类的内置方法 ***
使用__getitem__与__len__实现一个可迭代/可计算长度的对象
class Company(object): #魔法函数 def __init__(self, employee_list): self.employee = employee_list def __getitem__(self, item): return self.employee[item] # 不写它只有__getitem__的话还是能使用len求出长度的 def __len__(self): return len(self.employee) company = Company(["tom", "whw", "jane"]) # # for i in company.employee: # print(i) company1 = company[:2] # 调用__len__方法 print(len(company)) # 3 # 调用__getitem__方法 for em in company1: print(em) """ tom whw """
super方法
# 使用super函数 # 当前的类和对象可以作为super函数的参数使用,调用返回的对象的任何方法都是 # super函数会自动寻找他所需要的特性,直到返回一个AttributeError异常 class Bird: def __init__(self): self.hungry=True def eat(self): if self.hungry: print("eat") self.hungry=False else: print("no") class SongBird(Bird): def __init__(self): super(SongBird,self).__init__() self.sound="lalala" def sing(self): print(self.sound) sb=SongBird() sb.sing() # lalala sb.eat() # eat sb.eat() # no
使用__getitem__与super实现一个带有访问计数的list子类
# 子类化列表,字典和字符串 # 带有访问计数的列表 class CounterList(list): def __init__(self,*args): super(CounterList,self).__init__(*args) self.counter=0 def __getitem__(self, item): self.counter+=1 return super(CounterList,self).__getitem__(item) c1=CounterList(range(10)) print(c1) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] c1.reverse() print(c1) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] del c1[3:5] print(c1) # [9, 8, 7, 4, 3, 2, 1, 0] print(c1.counter) # 0 print(c1[4]+c1[2]) # 10 print(c1.counter) # 2
property函数
# property函数 # 将访问器函数被用作参数 class Rectangle: def __init__(self): self.width=0 self.height=0 def setSize(self,size): self.width,self.height=size def getSize(self): return self.width,self.height size=property(getSize,setSize) r=Rectangle() r.width=10 r.height=5 print(r.size) # (10, 5) r.size=150,100 print(r.width) # 150
静态成员方法与类成员方法
# 静态成员方法和类成员方法 class MyClass: def smeth(): print("This is a static method") smeth=staticmethod(smeth) def cmeth(cls): print("This is a class methon of'",cls) cmeth=classmethod(cls.cmeth) #装饰器 class MyClass1: @staticmethod def smeth(): print("This is a static method") @classmethod def cmeth(cls): print("This is a class method",cls) cla=MyClass1() cla.smeth() # This is a static method cla.cmeth() # This is a class method <class '__main__.MyClass1'>
__getattr__与__setattr__
#__getattr__、__setattr__ class Rectangle: def __init__(self): self.width=0 self.height=0 def __setattr__(self, key, value): if key=='size': self.width,self.height=value else: self.__dict__[key]=value def __getattr__(self, item): if item=='size': return self.width,self.height else: raise AttributeError d=Rectangle() d.height=10 d.width=10 print(d.__getattr__(item='size')) # (10, 10) d.__setattr__(key='size',value=(100,200)) print(d.height) # 200
__iter__与__next__方法简介
一个实现了__iter__方法的对象是可以迭代的,一个实现了__next__方法的对象则是迭代器!
# 迭代时候最好不要用列表,如果有很多值,列表会占用太多的内存 # 使用迭代器更通用,更简单,更优雅。 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 # 一个实现了__iter__方法的对象是可以迭代的,一个实现了__next__方法的对象则是迭代器 fibs=Fibs() for f in fibs: if f>1000: print(">>>",f) break #内建函数iter可以从可迭代的对象中获得迭代器 it=iter([1,2,3]) print(next(it)) print(next(it)) print(next(it)) """ >>> 1597 1 2 3 """
从迭代器得到序列
# 除了再迭代器和可迭代对象向上进行迭代,还可以把他们转换成序列 class TestIterator: value=0 def __next__(self): self.value+=1 if self.value>10:raise StopIteration return self.value def __iter__(self): return self t1=TestIterator() print(list(t1)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
八皇后问题
# 八皇后问题 # 首先寻找冲突 # 找到一种没有冲突的位置(没有皇后会被其他的皇后吃掉) def conflict(state,nextX): nextY=len(state) for i in range(nextY): if abs(state[i]-nextX) in (0,nextY-i): return True return False # 基本情况 # 周后一个皇后能够根据其他皇后的位置生成他自己能占据的位置 def queens(num,state): if len(state)==num-1: for pos in range(num): if not conflict(state,pos): yield pos # 需要递归的情况 # 递归函数需要假定所有的来自低层的结果都是正确的 # 假定将位置信息作为一个元组返回,需要修改基本情况也返回一个元组 # 这样一来,程序会从前面的皇后得到包含位置的元组信息,并且为后面的皇后提供当前皇后的每种合法位置信息 def queens2(num=8,state=()): for pos in range(num): if not conflict(state,pos): if len(state)==num-1: yield (pos,) else: for result in queens2(num,state+(pos,)): yield (pos,)+result # 打包输出 def prettyprint(solution): def line(pos,length=len(solution)): return '. '*(pos)+'X '+'. '*(length-pos-1) for pos in solution: print(line(pos)) import random prettyprint(random.choice(list(queens2(100))))
Python中类的归一化设计
isinstance与type
类与对象进阶
类与实例的查找顺序--mro查找
#新式类 class D: pass class E: pass class C(E): pass class B(D): pass class A(B, C): pass print(A.__mro__) """ (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>) """
访问类的私有属性
from chapter04.class_method import Date class User: def __init__(self, birthday): self.__birthday = birthday def get_age(self): #返回年龄 return 2018 - self.__birthday.year if __name__ == "__main__": user = User(Date(1990,2,1)) # 访问私有属性 print(user._Student__birthday) print(user.get_age())
Python对象的自省机制:__dict__ 与 dir()
—— 自省是通过一定的机制查询到对象的内部结构!
class Person: # 类属性 name = "user" class Student(Person): def __init__(self, scool_name): self.school_name = scool_name if __name__ == "__main__": user = Student("一了拉面") ### __dict__ # 通过__dict__查询属性 print(user.__dict__) # 设置 user.__dict__["school_addr"] = "火之国" print(user.school_addr) # 火之国 print(Person.__dict__) # {'__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None} print(user.name) # user ### dir方法 a = [1,2] print(dir(a)) # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
深入理解super函数 ***
super的继承顺序其实遵循mro的顺序(Python3中)!
from threading import Thread class MyThread(Thread): def __init__(self, name, user): self.user = user super().__init__(name=name) class A: def __init__(self): print ("A") class B(A): def __init__(self): print ("B") super().__init__() class C(A): def __init__(self): print ("C") super().__init__() class D(B, C): def __init__(self): print ("D") super(D, self).__init__() if __name__ == "__main__": print("mro>>>>>>",D.__mro__) """ mro>>>>>> (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) """ d = D() """ D B C A """
DRF的mixin模式
# mixin模式特点 # 1. Mixin类功能单一 # 2. 不和基类关联,可以和任意基类组合, 基类可以不和mixin关联就能初始化成功 # 3. 在mixin中不要使用super这种用法
上下文管理 ***
Python中的序列类之实现可切片对象 ***
列表切片的说明
# 模式[start:end:step] """ 其中,第一个数字start表示切片开始位置,默认为0; 第二个数字end表示切片截止(但不包含)位置(默认为列表长度); 第三个数字step表示切片的步长(默认为1)。 当start为0时可以省略,当end为列表长度时可以省略, 当step为1时可以省略,并且省略步长时可以同时省略最后一个冒号。 另外,当step为负整数时,表示反向切片,这时start应该比end的值要大才行。 """ aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17] print (aList[::]) # 返回包含原列表中所有元素的新列表 print (aList[::-1]) # 返回包含原列表中所有元素的逆序列表 print (aList[::2]) # 隔一个取一个,获取偶数位置的元素 print (aList[1::2]) # 隔一个取一个,获取奇数位置的元素 print (aList[3:6]) # 指定切片的开始和结束位置 aList[0:100] # 切片结束位置大于列表长度时,从列表尾部截断 aList[100:] # 切片开始位置大于列表长度时,返回空列表 aList[len(aList):] = [9] # 在列表尾部增加元素 aList[:0] = [1, 2] # 在列表头部插入元素 aList[3:3] = [4] # 在列表中间位置插入元素 aList[:3] = [1, 2] # 替换列表元素,等号两边的列表长度相等 aList[3:] = [4, 5, 6] # 等号两边的列表长度也可以不相等 aList[::2] = [0] * 3 # 隔一个修改一个 print (aList) aList[::2] = ['a', 'b', 'c'] # 隔一个修改一个 print (aList) #aList[::2] = [1,2] # 左侧切片不连续,等号两边列表长度必须相等 aList[:3] = [] # 删除列表中前3个元素 print (aList) del aList[:3] # 切片元素连续 print (aList) del aList[::2] # 切片元素不连续,隔一个删一个
自定义切片对象
import numbers
class Group: #支持切片操作 def __init__(self, group_name, company_name, staffs): self.group_name = group_name self.company_name = company_name self.staffs = staffs def __reversed__(self): self.staffs.reverse() def __getitem__(self, item): # 关键! cls = type(self) if isinstance(item, slice): return cls(group_name=self.group_name, company_name=self.company_name, staffs=self.staffs[item]) elif isinstance(item, numbers.Integral): return cls(group_name=self.group_name, company_name=self.company_name, staffs=[self.staffs[item]]) def __len__(self): return len(self.staffs) def __iter__(self): return iter(self.staffs) def __contains__(self, item): if item in self.staffs: return True else: return False staffs = ["whw1", "naruto", "whw2", "whw3"] group = Group(company_name="naruto", group_name="user", staffs=staffs) reversed(group) for user in group: print(user)
Python中的序列类之bisect管理排序序列 *****
—— 内部使用二分查找,业务中如果维护一个已排序的序列,尽量使用bisect!这样不用二次排序了!!!
import bisect from collections import deque # 用来处理已排序的序列,用来维持已排序的序列, 升序 inter_list = deque() bisect.insort(inter_list, 3) bisect.insort(inter_list, 2) bisect.insort(inter_list, 5) bisect.insort(inter_list, 1) bisect.insort(inter_list, 6) # 已经排好序了 print(inter_list) # deque([1, 2, 3, 5, 6]) # 如果插入4的话在哪个位置插入 print(bisect.bisect_left(inter_list, 4)) # 3 print(bisect.bisect_right(inter_list, 4)) # 3
什么时候不用list *
array
数组 —— 性能非常高。
# array, deque # 数组 import array a=list() print(a) # array和list的一个重要区别, array只能存放指定的数据类型 my_array = array.array("i") # int my_array.append(1) my_array.append(7) print(my_array)
deque
—— deque是线程安全的!
from collections import deque # 双端队列——支持列表的操作! d = deque([1,2,3,4]) print(d)#deque([1, 2, 3, 4]) # 右边添加 d.append(5)#deque([1, 2, 3, 4, 5]) print(d) # 左边添加 d.appendleft(6) print(d)#deque([6, 1, 2, 3, 4, 5]) # pop——不能给参数 print(d.pop())#5 print(d.popleft())#6 print(d)#deque([1, 2, 3, 4])
列表当作默认参数的一个坑 ***
class Company: def __init__(self, name, staffs=[]): self.name = name self.staffs = staffs def add(self, staff_name): self.staffs.append(staff_name) def remove(self, staff_name): self.staffs.remove(staff_name) if __name__ == "__main__": # 初始化的时候指定staffs列表的话,用传入的这个列表 com1 = Company("com1", ["whw1", "whw2"]) com1.add("whw3") com1.remove("whw1") print(com1.staffs) # ['whw2', 'whw3'] # 初始化的时候不指定staffs的话,用默认的哪个列表!—— 可能会与其他不指定staffs的对象用同一个列表! com2 = Company("com2") com2.add("whw") print(com2.staffs) # ['whw'] print("default>>",Company.__init__.__defaults__) # default>> (['whw'],) com3 = Company("com3") com3.add("whw5") print (com2.staffs) # ['whw', 'whw5'] print (com3.staffs) # ['whw', 'whw5'] print (com2.staffs is com3.staffs) # True
__new__与__init__区别简介
class User: def __new__(cls, *args, **kwargs): print (" in new ") return super().__new__(cls) def __init__(self, name): print (" in init") pass a = int()
# new 是用来控制对象的生成过程, 在对象生成之前 # init是用来完善对象的 # 如果new方法不返回对象, 则不会调用init函数
if __name__ == "__main__": user = User(name="whw")
property装饰器 ***
1. get和set的方法名称都要一样(age)
2. set方法返回的属性前面加个"_"
3. @property是针对get方法
4. @age.setter是针对set方法,是@property本身又创建了另一个装饰器
5. 直接可以这样stu.age=10对象名.方法名进行赋值,
6. 只定义getter方法,不定义setter方法是一个只读属性
from datetime import date, datetime class User: def __init__(self, name, birthday): self.name = name self.birthday = birthday self._age = 0 # 加上property装饰器可以像访问属性那样获取数据 @property def age(self): return datetime.now().year - self.birthday.year # 设置 @age.setter def age(self,value): if isinstance(value,int): if 0<value<120: self._age=valueelse: print("请输入合法的年龄") if __name__ == "__main__": user = User("whw", date(year=1995, month=2, day=22)) user.age = 30 print (user._age) # 30 print(user.age) # 25
__getattr__与__getattribute__方法
1、__getattr__:查找不到属性的时候进入这里(不考虑继承)。
2、__getattribute__:执行查找,无条件进入该魔法函数,即使查找的属性不存在(不考虑继承)。
#__getattr__, __getattribute__ #__getattr__ 就是在查找不到属性的时候调用 class User: def __init__(self,info={}): self.info = info # 查找不到属性的时候进入这里 def __getattr__(self, item): return self.info[item] # 执行查找, 无条件进入该魔法函数, 即使所查找的属性不存在 # def __getattribute__(self, item): # return "火之影" if __name__ == "__main__": user = User(info={"company_name":"china", "name":"whw"}) ### 如果不注释__getattribute__会打印: """ 火之影 火之影 """ print(user.company_name) print(user.my_name) ### 如果注释掉_getattribute__会: """ 1.有company_name会打印china 2.没有my_name会报错 """
__getattr__、__getattribute__与继承的关系
结论:优先使用双下划线的方法中的值!
#__getattr__, __getattribute__ #__getattr__ 就是在查找不到属性的时候调用 class UserBase: def __init__(self,age): self.age = age self.my_name = "Base" class User(UserBase): def __init__(self,info={}): self.info = info super().__init__(info) # 查找不到属性的时候进入这里 def __getattr__(self, item): return self.info[item] # 执行查找, 无条件进入该魔法函数, 即使所查找的属性不存在 # def __getattribute__(self, item): # return "火之影" if __name__ == "__main__": user = User(info={"company_name":"china", "name":"whw"}) ### 如果不注释__getattribute__会打印:———— 优先会调用自己的__getattribute__方法返回的数据! """ 火之影 火之影 """ print(user.company_name) print(user.my_name) ### 如果注释掉__getattribute__会打印: """ china Base """
Python属性描述符 **
个人Python描述符的博客
使用属性描述符校验
上面介绍property的时候提到的age在输入的时候需要校验, 实现是通过property的setter, 但是如果很多输入字段那么就要写很多重复的代码.这里就要用到属性描述符。
import numbers class IntField: # 数据描述符 def __get__(self, instance, owner): return self.value def __set__(self, instance, value): if not isinstance(value, numbers.Integral): raise ValueError("int value need") if value < 0: raise ValueError("positive value need") self.value = value def __delete__(self, instance): pass class User: age = IntField() if __name__ == "__main__": user = User() user.age = 30 # 进入数据描述符的__set__ setattr(user, 'age',18) # 进入数据描述符的__get__ print(user.age) # 进入数据描述符的__get__ user.__dict__["age"] = 18 print(user.__dict__["age"]) user.__dict__["age"] = 18 print(user.age) """ 输出全是18 """
简单的例子说明
class User: age = 1 if __name__ == "__main__": user = User() user.name = 30 # 保存在user对象的内存中 print(user.name) # 从user对象的内存中去取 user.age = 30 # 保存在user对象的内存中, 不影响类的内存中的值 print(user.age) # 进入数据描述符的__get__ user.__dict__["age"] = 18 print(user.__dict__["age"]) print (user.__dict__) """ 30 30 18 {'name': 30, 'age': 18} """
Python中属性的查找顺序 *****
完整的查找顺序如下 ***