python3之类和对象
一、面对对象(OOP)的三大特征
1 多态
无需知道对象是什么类型就能对它执行操作
不同类型的对象执行相同的操作,操作结果随着对象的类型而异
多态是方法的多态,属性没有多态
示例
def add(x,y): return x+y sum1=add(2,3) sum2=add('aaa','bbb') print(sum1) print(sum2) 数值返回两数字的和,字符串返回两个字符串拼接后字符串
2 封装
将对象的状态信息隐藏在对象内部,只对外提供必要的方法,不允许外部直接访问对象内部信息 向外部隐藏不必要的细节 无需知道对象的构造就能对它执行操作 通过私有属性、私有方法的方式实现封装,即属性、方法前面加上双下划线("__")
3 继承
创建一个与已有类相似的类,但是比原有类多了一些方法
新创建的类被称为子类(派生类),原有的类被称为父类(基类、超类)
子类可以继承父类,一个子类可以有多个父类
二、面对对象(OOP)术语简介
类(Class) 具有相同的属性或方法的对象的集合 定义了该集合中每个对象所共有的属性和方法 属性 类中定义的所有变量和方法,包括类变量和方法,实例变量 数据成员 类变量或者实例变量,用于处理类及其实例对象的相关的数据 类变量 类的所有实例共享的属性,可以通过"类名.变量名"直接访问 类变量通常不作为实例变量使用 方法 类中定义的函数 类对象 通过类定义的数据结构实例 支持两种操作:属性引用和实例化 属性引用(对属性点号运算) obj.属性名 实例对象 类实例化之后,类的具体实例对象 实例变量 实例化之后,每个实例单独拥有的变量 只作用于当前实例 以"self."开头,仅供各个实例对象调用 实例方法 实例化之后,每个实例都可以调用的方法 至少包含一个参数,第一个参数约定俗成命名为"self",通过它来传递实例的属性 方法重写 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写
示例
class Bird: #类变量 n = 999 #实例方法 def walk(self, name): print(name,'正在行走') #类方法 @classmethod def fly (cls, name): print('类方法fly:',cls) print(name,'正在飞') #类实例化,返回一个Bird对象,并赋值给变量u u = Bird() #传入实例变量name的值 u.walk('monkey') #通过实例对象调用类变量 print(u.n) #通过类名调用类变量 print(Bird.n) #通过类名调用类方法 Bird.fly('butterfly') class Person: hair='black' def set_name(self,name): self.name=name def get_name(self): return self.name def greet(self): print("hello,world!I'm %s" % (self.name)) #直接调用类变量 print(Person.hair) #类实例化,返回一个Persoon对象,并赋值给变量person person = Person() #通过实例调用类变量 print(person.hair) #调用实例方法并传入实例变量name的值 person.set_name('marry') #调用实例变量 print(person.name) #调用实例方法 print(person.get_name()) #调用实例方法 person.greet()
三、类定义
格式
class class_name: <statement-1> . . . <statement-N>
四、方法
1 实例方法
类中定义的方法默认是实例方法,和定义普通函数方法一致,但实例方法至少包含一个参数(通常是self),self参数会自动绑定调用者,指向调用者本身
调用实例方法时不需要为self参数传入值
示例
class Person: def set_name(self, name): self.name=name def get_name(self): return self.name def greet(self): print("hello,world!I'm %s" % (self.name)) #类实例化 person = Person() #调用实例方法并传入实例变量name的值,第一个参数self会自动绑定到调用者 person.set_name('marry') #调用实例变量 print(person.name) #调用实例方法 print(person.get_name()) #调用实例方法 person.greet()
2 类方法
使用@classmethod修饰的方法
类方法的cls参数会自动绑定类,执行该类
可以通过实例对象调用也可以通过类对象直接调用
调用类方法时不需要为cls传入参数
示例
class test: @classmethod def class_method(cls): print('类方法:',cls) @classmethod def classm(cls,name): print('类方法:',cls) print(name) #类实例化 T = test() #通过实例对象调用类方法,只有cls一个参数,指向test类,调用时不需要传入值 T.class_method() #该方法有两个参数,第一个cls指向test类,第二个参数需要调用时需要传入值 T.classm('jack') #通过类名调用类方法 test.class_method() test.classm('marry')
3 静态方法
使用@staticmethod修饰的方法
可以通过实例对象调用也可以通过类对象直接调用
调用时,需要传入参数,不会自动绑定
示例
class static: @staticmethod def static_method(p): print('静态方法:',p) #类实例化 S = static() #通过实例对象调用类方法,必须传入参数值 S.static_method('message') #通过类对象直接调用,必须传入参数值 static.static_method('info')
五、类内置方法
定义类时,前后都有双下划线定义的方法,都属于类的特殊方法
可以重写或者直接调用这些方法来实现特殊的功能
1 __init__(self) 方法
初始化方法(构造方法)
用于初始化实例化对象属性值
创建类的实例时就会调用该方法
若该方法有多个参数时,类实例化必须要传入参数
示例
class Rectangle(): def __init__(self, length, width): self.length = length self.width = width def getPeri(self): return ((self.length + self.width) * 2) def getArea(self): return (self.length * self.width) #类中定义了__init__方法且该方法不止一个参数,实例化时必须要传入length,width两个参数 R = Rectangle(6,5) print(R.getPeri()) print(R.getArea())
2 __str__(self) 和 __repr__(self) 方法
如果两个方法都定义了,print() 方法会调用 __str__ 方法
如果只想调用 __repr__ ,只需定义 __repr__ 方法
如果两个方法都没有定义,print()使用object基类中默认的 __str__ 方法,因为object是所有类的基类
__repr__ 和 __str__ 这两个方法都是用于显示的, __str__ 是面向用户的,而 __repr__ 面向程序员
只能输出字符串类型数据
使用中推荐至少有一个 __repr__ 方法
示例
class Repr: def __init__(self, name, age): self.name = name self.age = age class Repr1: def __init__(self, name, age): self.name = name self.age = age #重新定义__str__方法 def __str__(self): return 'this is __str__'+'\t\t'+self.name+":"+str(self.age) #重新定义__repr__方法 def __repr__(self): return 'this is __repr__'+'\t\t'+self.name+":"+str(self.age) class Repr2: def __init__(self,name,age): self.name = name self.age = age #重新定义__repr__方法 def __repr__(self): return f'this is __repr__, {self.name}:{self.age}' R = Repr('mack',22) #未定义__str__和__repr__方法,print(R)使用object基类中的默认__str__方法 print(R) print(R.__repr__) print(R.__str__) print() print() print() R1 = Repr1('jack',20) #调用__str__方法输出 print(R1) print(R1.__repr__) print(R1.__str__) print() print() print() R2 = Repr2('maryy',18) #调用__repr__方法输出 print(R2)
3 __del__(self) 方法
对象引用计数为0时,对象才会被回收,自动调用 __del__ 方法
即一个对象被多个变量引用时,del删除该对象时不会立即回收对象
一般出现在两个地方:
1、手动减少对象引用计数至0,被垃圾回收处理时调用
2、程序结束时调用
示例
class Del: def __init__(self, name, age): self.name = name self.age = age def __del__(self): print("%s对象已被删除" % self) D_1 = Del('maryy', 22) #变量D_2也指向对象 D_2 = D_1 #对象不会被回收,程序执行完才会调用__del__方法 del D_1 print(D_2.name,D_2.age)
class Del: def __init__(self, name, age): self.name = name self.age = age def __del__(self): print("%s对象已被删除" % self) D = Del('maryy', 22) #对象立即被回收,调用__del__方法 del D print("是否回收对象")
4 __dir__(self) 方法
返回对象内部所有的变量名和方法名组成的列表
dir(object)返回对象的 __dir__ 方法经过排序后的列表
示例
class Dir: var1 = 'value1' var2 = 'value2' def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return self.name+' '+str(self.age) def __del__(self): print("删除对象") D = Dir('maryy', 22) #通过实例化对象调用__dir__方法,返回类中所有实例属性组成的列表 print(D.__dir__()) #返回实例化对象调用__dir__方法经过排序后的列表 print(dir(D))
5 __dict__(self) 方法
返回对象内部的所有属性名和属性值组成的字典,该方法可以通过字典的语法来访问或修改指定属性的值
通过类名调用 __dict__ 方法,输出类中所有的属性,包括类属性,实例属性
通过实例化对象调用 __dict__ 方法,输出类中所有实例属性组成的字典
示例
class Dict: var1 = 'value1' var2 = 'value2' def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return self.name,self.age #通过类名调用__dict__方法,返回类中所有的属性组成的字典 print(Dict.__dict__) #类实例化 D = Dict('jack', 24) #通过实例化对象调用__dict__方法,返回类中所有实例属性组成的字典 print(D.__dict__) #以字典的语法调用__dict__方法访问属性值 print(D.__dict__['name']) print(D.__dict__['age']) #以字典的语法调用__dcit__方法修改属性值 D.__dict__['name'] = 'tom' D.__dict__['age'] = 25 print(D.__dict__['name']) print(D.__dict__['age'])
6 __getattr__(self) 、__setattr__(self) 、__delattr__(self) 方法
当对类和该类的所有父类(若有多层继承,沿着继承关系直到最后的基类)中未定义的属性(包括变量和方法)进行点号运算时,调用 __getattr__ 方法 如果可以找到该属性,则不调用此方法 仅在引用属性且属性不存在的时候才会触发 __getattr__ 方法 只要对属性(不管该属性是否在类中定义)赋值时都会调用 __setattr__ 方法 当在 __setattr__ 方法内对属性进行赋值时,不可使用self.attr=value,因为会再次调用 __setattr__ 方法,形成无限递归,导致堆栈溢出异常 一般在 __setattr__ 方法内对属性赋值,使用self.__dict__[attr]=value 删除属性时会调用 __delattr__ 方法
示例
class Set: def __init__(self,length,width): self.length = length self.width = width #重新定义__setattr__方法 def __setattr__(self,attr,value): print('---设置对象属性---') #传入的attr参数名是否为size,若是,则重新设置length,width的值 #否则新增一个名=attr,值=value的变量 if attr == 'size': self.__dict__['length'],self.__dict__['width'] = value else: self.__dict__[attr] = value #重新定义__getattr__方法 def __getattr__(self,attr): print('---访问对象属性---') #传入的attr参数名是否为size,若是,则返回原来length,width的值 #否则引发AttributeError异常 if attr == 'size': return self.length,self.width else: raise AttributeError #重新定义__delattr__方法 def __delattr__(self,attr): print('---删除对象属性---') #传入的attr参数名是否为size,若是,则修改length,width的值为0 #否则什么也不做 if attr == 'size': self.__dict__['length'] = self.__dict__['width']=0 else: pass #实例化类,调用__init__方法和__setattr__方法 S = Set(5,6) #返回所有实例属性组成的字典 print(S.__dict__) #size属性赋值,调用__setattr__方法 S.size = 8,9 print(S.__dict__) #删除size属性 del S.size print(S.__dict__) #big属性赋值,调用__setattr__方法 S.big = 1 print(S.__dict__)
7 序列相关的特殊方法
__len__(self) 返回元素的数量 __getitem__(self) 返回键对应的值 __setitem__(self) 设置给定键的值 __delitem__(self) 删除给定键对应的元素
示例
#key检查函数 #key必须是非负整数 #若key不是int类型,则引发TypeError #若0<key<=pow(26,3),则引发IndexError def check_key(key): if not isinstance(key, int): raise TypeError if (key<0) or (key >= pow(26,3)): raise IndexError class seq: def __init__(self): #存储被修改的数据 self.__chaged = {} #存储被删除元素的索引 self.__deleted = [] #获取元素个数 def __len__(self): print('---返回元素个数---') return pow(26,3) #获取索引对应的元素的值 def __getitem__(self, key): print('---获取key=%s对应元素的值---'% (key)) #调用key检查函数 check_key(key) #如果在self.__chaged找到数据,则返回已修改的值 if key in self.__chaged: return self.__chaged[key] #如果在self.__deleted中找到数据,说明元素被删除,返回None elif key in self.__deleted: return None #否则,根据计算规则返回元素,得到一个AAA,AAB,AAC,AAD,...,的序列 else: three = key//(pow(26,2)) two = (key-three*pow(26,2))//26 one = key%26 return chr(65+three)+chr(65+two)+chr(65+one) #设置索引对应元素的值 def __setitem__(self, key, value): print('---设置key=%s对应元素的值---'% (key)) #调用key检查函数 check_key(key) self.__chaged[key] = value #删除索引对应元素的值 def __delitem__(self, key): print('---删除序列key=%s对应元素的值---'% (key)) #调用key检查函数 check_key(key) #如果在self.__deleted中未找到key,则添加该key到删除列表中 if key not in self.__deleted: self.__deleted.append(key) #如果在self.__chaged中找到key,则删除该key if key in self.__chaged: del self.__chaged[key] #得到一个AAA,AAB,AAC,AAD,...,元素个数为pow(26,3)的序列 Seq = seq() #获取元素,调用__len__方法 print(len(Seq)) #获取key=1元素的值,调用__getitem__方法 print(Seq[1]) #删除key=1元素的值,调用__delitem__方法 del Seq[1] #获取key=1元素被删除后的值,调用__getitem__方法 print(Seq[1]) #获取key=2元素的值,调用__getitem__方法 print(Seq[2]) #修改key=2元素的值,调用__setitem__方法 Seq[2] = 'asd' #获取key=2元素被修改后的值,调用__getitem__方法 print(Seq[2])
class ArithemeticSequence: def __init__(self,start,step): self.start=start self.step=step self.myData={} #定义获取元素个数的方法 def __len__(self): print('---返回元素个数---') return len(self.myData) #定义获取值的方法 def __getitem__(self,key): print('---获取key=%s对应元素的值---'% (key)) try: return self.myData[key] except KeyError: return self.start+key*self.step #定义修改元素的方法 def __setitem__(self,key,value): print('---设置key=%s对应元素的值---'% (key)) self.myData[key]=value #定义删除元素的方法 def __delitem__(self, key): print('---删除序列key=%s对应元素的值---'% (key)) del self.myData[key] #得到一个等差序列实例对象 s=ArithemeticSequence(1,2) #获取元素个数,初始状态0个元素 print(len(s)) #获取key=3元素的值 print(s[3]) #修改key=3元素的值 s[3]=100 #获取key=3元素被修改后的值 print(s[3]) #获取元素个数,只有一个元素 print(len(s)) #删除key=3元素的值 del s[3] #获取元素个数,0个元素 print(len(s))
六、类继承
格式
class SubClass(SuperClass1,SuperClass2,..): <statement-1> . . . <statement-N> 其中 SubClass是子类,SuperClass1,SuperClass2,..是父类(基类|超类),多个父类以逗号隔开 若多个父类中有相同的属性,则使用(SuperClass1,SuperClass2,..)中排在最前面的父类的属性,所以不是很有必要,尽量不使用继承多个父类 object是所有类的直接父类或间接父类
示例
class Fruit: def info(self): print('水果:%s,重量:%s'%(self.name,self.weight)) class Food: def taest(self): print('不同食物口感不同') class Apple(Fruit,Food): def __init__(self,name,weight): self.name=name self.weight=weight A=Apple('apple',5) A.info() A.taest()
1 重写父类方法
示例
class Father(object): def __init__(self, name): self.name = name print('父类__init__方法') def getName(self): print('父类getName方法') return 'Father '+self.name class Son(Father): #重写父类中的getName方法,父类的getName方法被覆盖 def getName(self): print('子类getName方法') return 'Son name: '+self.name son = Son('test_class') print(son.getName())
class Father(object): def __init__(self, name): self.name = name print('父类__init__方法') def getName(self): print('父类getName方法') return 'Father name:'+self.name class Son(Father): #重写父类的 __init__ 方法,父类中的 __init__ 方法会被覆盖 def __init__(self, name): self.name = name print('子类__init__方法') #重写父类中的 getName 方法,父类的 getName 方法被覆盖 def getName(self): print('子类getName方法') return 'Son name:'+self.name son = Son('test_class') print(son.getName())
2 子类中调用父类方法
格式
父类名.方法名(参数)
参数必须和父类方法中参数一致,包括self参数
示例
class Father(object): def __init__(self, name): self.name = name print('父类__init__方法') def getName(self): print('父类getName方法') return 'Father name:'+self.name class Son(Father): #重写父类中的getName方法 def getName(self): print('子类getName方法') #调用父类方法 print(Father.getName(self)) return 'Son name:'+self.name son = Son('test_class') print(son.getName())
3 使用super()函数继承父类的 __init__(self) 方法
格式
super().__init__(self): 参数包括父类方法中除了self参数的所有参数
示例
class Father(object): def __init__(self, name): self.name = name print('父类__init__方法') def getName(self): print('父类getName方法') return 'Father name: '+self.name class Son(Father): def __init__(self, name): #继承父类中的__init__方法 super().__init__(name) print('子类__init__方法') def getName(self): print('子类getName方法') #子类方法中调用父类方法 print(Father.getName(self)) return 'Son name: '+self.name son = Son('test_class') print(son.getName())
七、枚举类
类的对象有限而且固定,例如季节,星期几
枚举类不能用来实例化,且一旦定义创建,不能在外部修改成员值
可以使用==或者is比较成员,并且可以迭代枚举
包括
enum.Enum
创建枚举常量的基类,value为任意数据类型
enum.IntEnum
创建枚举常量的基类,value只能是整型数字
enum.IntFlag
创建枚举常量的基类,value只能是整型数字
使用按位运算符对其进行组合而不会丢失其IntFlag成员资格
enum.Flag
创建枚举常量的基类,value为任意数据类型
使用按位运算符对其进行组合而不会丢失其IntFlag成员资格
enum.unique()
name不能相同,value也不相同
enum.auto
成员值随机生成,初始值1
示例
from enum import Enum, IntEnum, unique #枚举类的装饰器,若有value相同则引发ValueError错误并附带相关细节信息 @unique class Season(Enum): SPRING = "春天" SUMNNER = '夏天' FALL = '秋天' WINTER = '冬天' @unique class color(IntEnum): red = 1 yellow = 2 green = 3 pink = 4 class color2(IntEnum): red = 1 beown = 1 yellow = 2 green = 3 ping = 4 #获取Season枚举类成员SPRING的值 print(Season.SPRING.value) #获取成员的name print(Season.SPRING.name) #通过成员value获取成员的name print(Season('春天')) #获取成员name=SPRING的信息 print(repr(Season.SPRING)) #获取成员name=SPRING属于哪个枚举类 print(type(Season.SPRING)) menber = Season.SPRING #获取成员的name print(menber.name) #获取成员的value print(menber.value) #枚举类成员比较 print(Season.SPRING == Season.SPRING) print(Season.SPRING is Season.SPRING)