Python基础(6)- 类和对象(使用、继承、派生、组合、接口、多态、封装、property、staticmethod、classmethod、反射、slots、上下文管理协议、元类)
来源:互联网 发布:jsp链接数据库 编辑:程序博客网 时间:2024/06/06 02:04
一、初识类和对象
在python3中类型就是类
先定义类在产生相对应的对象,也就是现有了概念再有了实体
class Garen:
camp = ‘Demacia’
def attack(self):
print(‘attack’)
1、如何使用类
在python3:
1、所有的类都是新式类,即默认都是继承object类
在python2中:
1、新式类,必须明确写出继承object类
2、经典类,没有写出继承object类
#方式一:实例化x = int(10)print(10)obj = Garen()print(obj)#方式二:引用类的类的变量和类的函数Garen.camp
2、如何使用对象(实例)
对象的使用方法:
1、属性引用:对象名.属性名(对象的属性:变量和函数)
class Garen: camp = 'Demacia' def __init__(self, nickname): self.nick = nickname def attack(self): print('attack')g1 = Garen('草丛伦') #会自动触发__init__函数g2 = Garen('猥琐伦')Garen.attack(123) #此处必须传参数,因为调用的是函数
调用绑定方法,会把自己作为第一个参数传入
g1.attack() #此处不需传参就可以调用,系统会将self = g1,因为调用的是实例化后的g1对象;
所以,只要是有对象进行调用,就会触发自动传值,将对象作为第一个对象传入,也就是将g1传入self
总结:
类:
第一种用途-实例化
第二种用途-引用名字(类名.变量名、类名.函数名)
实例:引用名字(实例名.类的变量,实例名.绑定方法,实例名.实例自己的变量名)
对象(实例):
对象的属性:对象本身就只有特征(变量)
对象的用法:属性引用
s1.id、s1.name、s1.sex、s1.province
3、类与对象的名称空间及绑定方法
类与对象的名称空间
类的名称空间:Student.__dict__
对象的名称空间:s1.__dict__
对象在调用方法或属性时:先从对象的名称空间找,再从类的名称空间找
tips:
类的变量是与对象共有的
类与对象的绑定方法
类的绑定方法与对象的绑定方法不共有:
绑定方法的核心在于”绑定”,唯一绑定一个确定的对象,谁调用就作用在谁上
4、类的继承与派生
继承
1、什么是继承
#python支持多继承,__bases__查看父类class ParentClass1: passclass ParentClass2: passclass SubClass1(ParentClass1): passclass SubClass2(ParentClass1,ParentClass2): pass
2、继承与抽象(先抽象再继承):
抽象即抽取类似或者说比较像的部分
抽象分成两个层次:
①
②将人、猪、狗这三个类比较像的部分抽取成父类
抽象最主要的作用是划分类别
继承:是基于抽象的结果,通过编程语言去实现,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构
例子:
class Animal: def __init__(self,name,age): self.name = name self.age = age def walk(self): print('%s is walking' %self.name ) def say(self): print('%s is saying' %self.name )class People(Animal): passclass Pig(Animal): passclass Dog(Animal): passp1 = People('obama',50)p1 = People('obama',50)p1 = People('obama',50)
3、类的继承原理
继承顺序:
类是经典类,多继承的情况下,会按照深度优先方式查找
类是新式类,多继承的情况下,会按照广度优先方式查找
#广度优先查找方式:class A(object): def test(self): print('from A')class B(A): def test(self): print('from B')class C(A): def test(self): print('from C')class D(B): def test(self): print('from D')class E(C): def test(self): print('from E')class F(D,E): def test(self): print('from F')f1 = F()f1.test()print(F.__mro__) #只有新式类中有这个方法,用来查看继承顺序(方法解析顺序MRO)#逐层注释#F—>D—>B—>E—>C—>A—>object:广度优先
#深度优先查找方式:class A(object): def test(self): print('from A')class B(A): def test(self): print('from B')class C(A): def test(self): print('from C')class D(B): def test(self): print('from D')class E(C): def test(self): print('from E')class F(D,E): def test(self): print('from F')f1 = F()f1.test()#逐层注释#D—>B—>A—>E—>C:深度优先
子类中调用父类的方法
方法一:父类名.父类方法
方法二:super()
不使用super()的惨痛教训
super()在python2中的用法:
1、super(自己的类,self).父类的函数名
2、super()只能用于新式类
派生
需求:子类派生的新方法是在父类的基础上变更,而不是整个重写
派生:子类继承了父类的属性,然后衍生出自己新的属性,
如果子类衍生出的新属性与父类的某个属性名字相同,
那么再调用子类的这个属性,就以子类自己这里的为准了
解决:父类名.方法
5、组合
代码重用的关系:
1、继承——什么’是’什么
2、组合——什么’有’什么
class Teacher: def __init__(self,name,sex,course): self.name = name self.sex = sex self.course = courseclass Course: def __init__(self,name,price,peroid): self.name = name self.price = price self.peroid = peroid#课程python_obj = Course('python',15800,'7m')#老师'有'课程t = Teacher('egon','male',python_obj)
6、接口与归一化设计与抽象类
接口
什么是接口:
python中没有接口的概念,用继承的方式来模拟接口接口的好处:归一化设计
例:
class Animal: def run(self): pass def speak(self): passclass People(Animal): def run(self): print('people is running!') def speak(self): print('people is speaking!')class Pig(Animal): def run(self): print('pig is running!') def speak(self): print('pig is speaking!')
抽象类
import abcclass Animal(metaclass=abc.ABCMeta): @abc.abstractmethod #表示这个方法必须被子类实现 def run(self): pass @abc.abstractmethod #表示这个方法必须被子类实现 def speak(self): pass
以上所有都是为了让python模拟实现类似于java中接口的功能,也就是定义一个接口类,接口类中有方法,所有继承接口类的子类必须定义接口类中的方法,不然报错
6、多态与多态性
多态:指一类事物的多种形态,就叫多态,并用继承的形式体现
多态性:定义统一的接口-可以传入不同类型的值,但是调用的逻辑都一样,执行的结果却不一样
多态性的好处:
1、增加灵活性
2、增加扩展性
#多态class Animal: def __init__(self,name,age): self.name = name self.age = age def walk(self): print('%s is walking' %self.name ) def say(self): print('%s is saying' %self.name )class People(Animal): passclass Pig(Animal): passclass Dog(Animal): passpeo1 = People()pig1 = Pig()
#多态性def func(obj): obj.run()func(peo1)func(pig1)
总结:
多态:定义角度而言
多态性:使用角度而言
7、封装
要封装什么:数据的封装、方法的封装
为什么要封装:
封装数据的主要原因是:保护隐私
封装方法的主要原因是:隔离复杂度
封装分为两个层面:
1、第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
2、第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
__名字,这种语法,只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果了
__名字形式,不能被子类覆盖,因为在定义阶段名字的形式会变形,变为_类名名字,因为类名是父类的类名,子类不能改写,所以不能被子类覆盖
8、property(特性)
什么是特性(property)
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值被property装饰的属性,如sex,分成三种
1、property
2、sex.setter
3、sex,deleter一旦一个属性被修饰之后,它就优先于对象
import mathclass Circle: def __init__(self,radius): self.radius = radius def area(self): return math.pi * self.radius ** 2 #计算面积 def perimeter(self): return 2 * math.pi * self.radius #计算周长c = Circle(7)print(c.radius)print(c.area())print(c.perimeter())
上面的代码中,面积和周长应该是圆的一个属性,而不是一种绑定方法
改:import mathclass Circle: def __init__(self,radius): self.radius = radius @property #area=property(area) def area(self): return math.pi * self.radius ** 2 #计算面积 @property def perimeter(self): return 2 * math.pi * self.radius #计算周长加上装饰器(property)之后就变成了一个属性,不需要用()进行调用,而是直接和调用属性的方式一样print(c.area)print(c.perimeter)
另一种玩法:
class People: def __init__(self,name): self.__Name = name @property def name(self): return self.__Namep1 = People('cobila')print(p1.name)p1.name = 'egon' #报错的,因为它真实存在的位置是self.__Name#需求:被property装饰过的方法,用户想要主动修改class People: def __init__(self,name,sex): self.__Name = name self.__Sex = sex @property def sex(self): return self.__Sex @sex.setter def sex(self,value): #设定value的值为字符串 if not isinstance(value,str): raise TypeError("性别必须是字符串类型!") self.__Sex = valuep1 = People('cobila','male')p1.sex = 'female'print(p1.sex)
类比:还有@sex.deleter
9、staticmethod(静态方法)
statimethod特点:
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果
statimethod作用:
python为我们内置了函数staticmethod来把类中的函数定义成静态方法,
statimethod修饰过的方法就是给类用的,不与任何对象绑定,也就是说不是对象的绑定方法,对象不能调用(实际上也可以调用的到),就算对象调用了也是用类来调用的
class Foo: def spam(self,x,y,z): print(x,y,z)Foo.spam(123)class Foo: @staticmethod def spam(x,y,z): print(x,y,z)Foo.spam(1,2,3)
总结
但凡是定义在类的内部,并且没有被任何修饰器修饰过的方法,都是绑定方法,有自动传值功能
但凡是定义在类的内部,并且staticmethod修饰器修饰过的方法,都是解除绑定方法,实际上就是函数,没有自动传值的功能
10、classmethod(类方法)
例子中用于子类继承父类之后,调用方法的是父类,而应该是子类,所以要用到classmethod
class Foo: @classmethod #把一个方法绑定给类,类.绑定到类的方法(),会把类本身当做第一个参数自动传递给绑定到类的方法 def test(cls,x): print(cls,x) #拿到一个类的内存地址后,可以实例化或者引用类的属性Foo.test(123)__str__的用法:定义在类内部,必须返回一个字符串类型什么时候触发__str__执行:打印由这个类产生的对象时会触发执行class People: def __init__(self,name,age): self.name = name self.age = age def __str__(self): return "<name:%s,age:%s>"%(self.name,self.age)p1 = People('egon',18)print(p1)
*11、反射
一、isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object): passobj = Foo()isinstance(obj, Foo)
二、issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object): passclass Bar(Foo): passissubclass(Bar, Foo)
三、反射:通过字符串的形式操作对象相关的属性;python中的一切事物都是对象(都可以使用反射)
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
class People: country = 'China' def __init__(self,name): self.name = namep1 = People('egon')People.country
反射的概念就是用字符串的形式来访问(调用)属性
1、
hasattr(p,'name')#判断object中有没有一个name字符串对应的方法或属性print('name' in p.__dict__)print(hasattr(p,'name'))
2、
getattr(object, name, default=None)getattr(p,'xxxxx')
3、setattr(x, y, v)
4、delattr(x, y)
5、反射当前模块属性
import systhis_module = sys.modules[__name__] #拿到当前模块的对象hasattr(thsi_module,'xxx')getattr(thsi_module,'xxx')delattr(thsi_module,'xxx')
四、反射的应用
反射的精髓:通过字符串获取需要的属性
好处一:
def add(): print('add')def change(): print('change')def search(): print('search')def delete(): print('delete')func_dict={ 'add':add, 'change':change, 'search':search, 'delete':delete}while True: cmd = input(">>>").strip() if not cmd : continue if cmd in func_dict: #hasattr() func = func_dict.get(cmd) #getattr() func()
用反射改进:
def add(): print('add')def change(): print('change')def search(): print('search')def delete(): print('delete')this_module = sys.modules[__name__]while True: cmd = input(">>>").strip() if not cmd : continue if hasattr(this_module,cmd): func = getattr(this_module,cmd) func()
好处二:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,
lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?
即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
好处三:动态导入模块(基于反射当前模块成员)
12、attr系列
__setattr__、__delattr__、__getattr__
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item)#__setattr__添加/修改属性会触发它的执行f1=Foo(10)print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值f1.z=3print(f1.__dict__)#__delattr__删除属性的时候会触发f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作del f1.aprint(f1.__dict__)#__getattr__只有在使用点调用属性且属性不存在的时候才会触发f1.xxxxxx
三者的用法演示:
①定制自己的数据类型
二次加工标准类型
基于继承的原理:来定制自己的数据类型(继承标准类型)
class List(list): def append(self,p_object): if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) #派生,调用父类的方法l = List([1,2,3])print(l)l.append(4)print(l)需求:不能用继承来实现open的功能基于授权的原理:实现授权的关键点就是覆盖__getattr__方法f = open('a.txt','w')print(f)class Open: def __init__(self,filepath,m='r',encode='utf-8'): self.x = open(filepath,mode=m,encoding=encode) self.filepath = filepath self.mode = mode self.encoding = encoding def write(self,line): t=time.strftime('%Y-%m-%d %X') self.x.write(t,line) def __getattr__(self,item): #找不到的时候触发getattr,去真实的文件句柄中去找 return getattr(self.x,item)f = Open('b.txt','w')print(f)f.write('1111\n')print(f.read())
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。
其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖getattr方法
练习一
class List: def __init__(self,seq): self.seq=seq def append(self, p_object): ' 派生自己的append加上类型检查,覆盖原有的append' if not isinstance(p_object,int): raise TypeError('must be int') self.seq.append(p_object) @property def mid(self): '新增自己的方法' index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq)l=List([1,2,3])print(l)l.append(4)print(l)# l.append('3333333') #报错,必须为int类型print(l.mid)#基于授权,获得insert方法l.insert(0,-123)print(l)
练习二
class List: def __init__(self,seq,permission=False): self.seq=seq self.permission=permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq)l=List([1,2,3])# l.clear() #此时没有权限,抛出异常l.permission=Trueprint(l)l.clear()print(l)#基于授权,获得insert方法l.insert(0,-123)print(l)
13、slots与迭代器协议
在写大型框架的时候会用到
描述符(__get__,__set__,__delete__)
一、__setitem__,__getitem,__delitem__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item)f1=Foo('egon')f1['age']=18f1['age1']=19del f1.age1del f1['age']f1['name']='alex'print(f1.__dict__)
二、__slots__ (英文slot意思为槽)
1.__slots__是什么
是一个类变量,变量值可以是列表,元组,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
*刨根问底
class Foo: __slots__=['name','age']f1=Foo()f1.name='alex'f1.age=18print(f1.__slots__)f2=Foo()f2.name='egon'f2.age=19print(f2.__slots__)print(Foo.__dict__)#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存
三、 __next__和__iter__实现迭代器协议
from collections import Iterable,Iteratorclass Foo: def __init__(self,start): self.start = start def __iter__(self): return self def __next__(self): # 这样的缺点就是初始值0不能得到,做以下改进 # self.start += 1 # return self.start #改进版,就可以返回0这个初始值了 if self.start > 10 : raise StopIteration n = self.start self.start += 1 return nf = Foo()f.__iter__()f.__next__()print(isinstance(f,Iterable))print(isinstance(f,Iterator))print(next(f)) #f.__next__()for i in f: # res = f.__iter__() #next(res) #直至抛出异常obj = f.__iter__()
14、上下文管理协议
一、 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,
因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
二、上下文管理协议
with open('a.txt','r') as f: pass
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): #type、value、traceback print('with中代码块执行完毕时执行我啊')with Open('a.txt') as f: # Open('a.txt').__enter__ print('=====>执行代码块') # print(f,f.name)exc_type = 类型(异常类型)exc_val = 值(异常值)exc_tb = 追踪信息
15、元类
typer—->类—->对象
创建类的两种方式
方式一:使用class关键字
方式二(就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建
准备工作:创建类主要分为三部分
1 类名
2 类的父类
3 类体
步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),
我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,
只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典
步骤二:调用元类type(也可以自定义)来产生类Chinense
我们看到,type 接收三个参数:
第 1 个参数是字符串 ‘Foo’,表示类名
第 2 个参数是元组 (object, ),表示所有的父类
第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法
补充:若Foo类有继承,即class Foo(Bar):…. 则等同于type(‘Foo’,(Bar,),{})
4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,
用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)
自定制元类
class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): print(self) print(class_name) print(class_bases) print(class_dic) type.__init__(self,class_name,class_bases,class_dic)class Foo(metaclass=Mymeta): #指定Foo继承自定制的元类Mymeta x=1 def run(self): print('running')要实现的效果:Foo = Mymeta('Foo',(object,),{'x':1,'run':run的内存地址})
实现定制类——需求:强制定义类的时候必须写注释
class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): print(self) print(class_name) print(class_bases) print(class_dic) type.__init__(self,class_name,class_bases,class_dic) for key in class_dic: if not callable(class_dic[key]): continue if not class_dic[key].__doc__: raise TypeError("没写注释,赶紧写!")class Foo(metaclass=Mymeta): #指定Foo继承自定制的元类Mymeta x=1 def run(self): print('running')
- Python基础(6)- 类和对象(使用、继承、派生、组合、接口、多态、封装、property、staticmethod、classmethod、反射、slots、上下文管理协议、元类)
- python @ property classmethod staticmethod
- python @property,@staticmethod,@classmethod 使用例子
- python-静态方法staticmethod、类方法classmethod、属性方法property
- 【Python笔记】装饰器语法糖(@staticmethod/@classmethod/@property)原理剖析及使用场景说明
- [基础] - Python中 @staticmethod 和 @classmethod
- python staticmethod和classmethod
- Python @staticmethod和@classmethod
- 【python】classmethod 和staticmethod
- python classmethod和staticmethod
- @staticmethod @classmethod @property使用【1】
- python基础-abstractmethod、__属性、property、setter、deleter、classmethod、staticmethod
- python @classmethod和@staticmethod 装饰器使用
- python的 @staticmethod,@classmethod和@property的区别
- python @classmethod和@staticmethod、@property以及装饰器
- python 静态方法staticmethod和类方法classmethod
- python 类中@staticmethod,@classmethod和实例方法
- Python中classmethod()和staticmethod()的用法
- python实现贝叶斯分类器
- spring事务管理
- 事件的分发机制(总结)
- RSA密钥生成方式
- Android学习笔记之常用的小知识点
- Python基础(6)- 类和对象(使用、继承、派生、组合、接口、多态、封装、property、staticmethod、classmethod、反射、slots、上下文管理协议、元类)
- 编写一个从字符串到长整形的转换函数
- Android 使用Scheme实现从网页启动APP
- Android中内存泄漏和内存溢出
- 人工智能将如何变革教育?【智库2861】
- PAT乙级 1042. 字符统计(20)
- (转)在Windows上安装GPU版Tensorflow
- 织梦中在线显示pdf文件的方法
- PAT乙级 1043. 输出PATest(20)