面向对象编程
object(对象)
什么是对象
对象是指现实中的物体或实物
什么是面向对象
把一切当作对象(实例),让对象和对象之间创建关联关系
对象都有什么特征
对象有不少属性(名词),用变量记录属性
姓名,年龄,性别等
对象有不少行为(动做,动词),用函数(方法)表示行为
学习,吃饭,睡觉,踢球,工做
什么是类class:
拥有相同属性和行为的对象分为一组,即为一个类
类是用来描述对象的工具,用类能够建立同类对象
类的建立语句:
语法:class 类名(继承列表):
'''类的文档字符串'''
实例方法定义(类内的函数称为方法method)
类变量定义
类方法定义
静态方法定义
做用:
一、建立一个类
二、用于描述此类对象的行为和属性
三、类用于建立此类的一个或多个对象(实例)python
class Dog:#定义一个类,类名为Dog pass dog1 = Dog()#建立Dog类的一个对象 print(id(dog1))#140399880349568 dog2 = Dog()#建立Dog类的另外一个对象 print(id(dog2))#140399880349848 #相似于以下语句: int1 = int() int2 = int ()
类 和 对象
类 | 对象 实例
class | object instance
构造函数
表达式:类名([建立传参列表])
做用:建立这个类的实例对象,并返回此实例对象的引用关系
实例(对象)说明
实例有本身的做用域和名字空间,能够为该实例添加实例变量(属性)
实例能够调用类方法和实例方法
实例能够访问类变量和实例变量
实例方法:
语法:calss 类名(继承列表):
def 实例方法名(self,参数1,参数2...):
'''实例方法的的文档字符串'''
语句块
做用:用于描述一个对象的行为,让此类型的所有对象都拥有相同的行为
说明:
一、实例方法实质是函数,是定义在类内的函数
二、实例方法至少有一个形参,第一个形参表明调用这个方法的实例,通常命名为”self“
实例方法的调用语法:
一、实例.实例方法名(调用传参)
二、类名.实例方法名(实例,调用传参)编程
#此实例示意如何用实例方法(method)来描述Dog类的行为 class Dog(): def eat(self,food): '''此方法用来描述小狗吃东西的行为''' print("小狗正常吃:",food) def sleep(self,hour): print("小狗睡了",hour,'小时') dog1 = Dog()#建立一个Dog的类的实例 dog1.eat("狗粮") dog1.sleep(1) Dog.eat(dog1,"狗粮")#第二种方法调用实例方法 #对象不能调用类内没有的方法
属性 attribute(也叫实例变量)
每一个实例均可以有本身的变量,此变量称为实例变量(也叫属性)
属性的使用语法:实例名.属性名
赋值规则:
一、首次为属性赋值,则建立此属性
二、再次为属性赋值,则必改变属性的绑定关系
做用:用来记录对象自身的数据less
class Dog: pass #建立第一个对象 dog1 = Dog() dog1.kinds = '京巴' #添加属性kinds dog1.color = '白色' #添加属性color dog1.color = '京巴' #改变属性的绑定关系
实例方法和实例变量(属性)结合使用编程语言
class Dog: def eat(self,food): print("%s的%s正在吃%s"%(self.color,self.kinds,food)) dog1 = Dog() dog1.kinds = '京巴' #添加属性kinds dog1.color = '白色' #添加属性color dog1.eat("狗粮")#该语句不能放在kinds和color属性被添加以前 dog2 = Dog() dog2.kinds = '哈士奇' #添加属性kinds dog2.color = '黑色' #添加属性color dog2.eat("包子")
删除属性
用del语句能够删除一个对象的实例变量
语法:
del 对象.实例变量名
示例:
class Cat:
pass
c1 = Cat() #建立实例对象
c1.color = "白色"#添加属性
del c1.color #删除属性
print(c1.color)#属性错误
初始化方法:
做用:对新建立的对象添加实例变量(属性)或相应的资源
语法:
class 类名(继承列表):
def __init__(self[,形参列表]):
语句块
说明:
一、初始化方法名必须为__init__不可改变
二、初始化方法会在构造函数建立实例后自动调用,且将实例自身经过第一个参数self传入__init__方法
三、构造函数的实参将经过__init__方法的形参传入__init__方法中
四、初始化方法内部若是须要返回,则只能返回Noneide
class Car(): def __init__(self,c,b,m):#此方法只能怪返回None print("__init__方法被调用") self.color = c self.brand = b self.model = m #return 1 #TypeError: __init__() should return None, not 'int' car = Car("红色","奥迪","A4")#Car()构造函数首先建立一个空的(没有属性的对象),而后把这个对象传递给__init__方法
析构方法:
语法:class 类名(继承列表):
def __del__(self):
语句块
说明:析构方法在对象销毁时被自动调用
做用:清理此对象所占用的资源
python不建议在析构方法中作任何事情,由于对象销毁的时间难以肯定
预置实例属性:
__dict__属性:
此属性绑定一个存储此实例自身实例变量(属性)的字典,也能够存储类中的变量,文档字符串,方法
示例:
class Dog():
pass
dog = Dog()
print(dog.__dict__)#{}
dog.kinds = "aa"
print(dog.__dict__)#{"kinds":"aa"}
dog.__dict__["color"]= "red"
__class__属性:
此属性用来绑定建立此实例的类
做用:能够借助此属性来访问建立此实例的类
示例:
class Dog()
pass
dog1=Dog()
dog2 = dog1.__class__()#等同于dog2 = Dog()
面向对象的综合示例
有两我的:
一、姓名:张三 年龄:35
二、姓名:李四 年龄:38
行为:
一、交别人学东西teach
二、赚钱
三、借钱
事情:
张三 教 李四 学 python
李四 教 张三 学 跳皮筋
张三 上班赚了 1000 元钱
李四 向 张三 借了 200 元钱函数
class Person(): '''人类,用于描述人的行为和属性''' def __init__(self,name,age): self.name = name self.age = age self.money = 0 def teach(self,other,things): print(self.name,"教",other.name,"学",things) def make_money(self,money): self.money+=money print(self.name,"上班赚了",money,"元钱") def borrow_money(self,other,money): if other.money > money:# print(self.name,"向",other.name,"借了",money,"元钱") other.money -= money self.money += money print(other.name,"没有钱借给",self.name) def show_info(self): print(self.age,"岁的",self.name,"存有",self.money,"元钱") p1 = Person("张三",35) p2 = Person("李四",38) p1.teach(p2,"python") p2.teach(p1,"跳皮筋") p1.make_money(1000) p2.borrow_money(p1,200) p1.show_info() p2.show_info() # 张三 教 李四 学 python # 李四 教 张三 学 跳皮筋 # 张三 上班赚了 1000 元钱 # 李四 向 张三 借了 200 元钱 # 张三 没有钱借给 李四 # 35 岁的 张三 存有 800 元钱 # 38 岁的 李四 存有 200 元钱
用于判断类的函数:
isinstance(obj,class_or_tuple)返回一个对象obj是否为某个类class或某些类的实例,若是是返回True,不然返回False
type(obj) 返回对象的类型
示例:
class Dog:
pass
class Cat:
pass
animal = Dog()
isinstance(animal,Dog) #True
isinstance(animal,Cat) #False
isinstance(animal,(Cat,int,list)) #False
isinstance(animal,(Cat,int,list,Dog)) #True工具
类变量 class variable(也叫类属性):
类变量是类的属性,此属性属于类
做用:用来记录类的相关数据
说明:
一、类变量能够经过类直接访问
二、类变量能够经过类的实例直接访问
三、类变量能够经过类的实例的__class__属性访问
实例:
class Human:
count = 0 #建立类变量学习
class Human: count =0 #建立一个类变量 print("Human的类变量count=",Human.count)#0 h1 = Human() print("用h1对象访问Human的count变量",h1.count)#0 print("用h1对象的__class__属性访问Human的count变量",h1.__class__.count)#0 h1.count = 100 #此处hi建立实例变量,经过实例不能经过这样的方式修改类变量,能够经过__class__属性来修改 print(h1.count) #100,会优先寻找实例变量,若是没有实例变量才会访问到类变量 print(Human.count) #0 print(h1.__class__.count) #0
类变量的应用案例:
用类变量来记录对象的个数测试
class Car: count = 0 def __init__(self,info):#建立类变量,用来记录汽车对象的总数 print(info,"被建立") self.data = info#记录传入的数据 self.__class__.count +=1 #让车的总数加1 def __del__(self): print(self.data,"被销毁") self.__class__.count -=1#让车的总数减1 print("当前汽车总数是",Car.count) car1 = Car("奥迪") print(Car.count) car2 = Car("奔驰") car3 = Car("大众") print(Car.count)
类的文档字符串:
类内的一个没有赋值给任何变量的字符串是类的文档字符串
说明:
一、类的文档字符串用类的__doc__属性能够访问
二、类的文档字符串能够用help()函数查看
类的__slots__列表:
做用:限定一个类的实例只能有固定的属性(实例变量),一般为了防止错写属性名而发生运行时错误
说明:含有__slots__列表的类建立的实例对象没有__dict__属性,即此实例不用字典来保存对象的属性(实例变量)spa
class Student: __slots__ = ["name","score","aa"]#此处表示该实例对象最多只能有两个实例变量 def __init__(self,name,score): self.name = name self.score = score s1 = Student("小张",58) print(s1.score) s1.socre = 100 #此处写错了属性名,但在运行时不会报错 ,若是添加了__slots__变量后就会产生AttributeError: 'Student' object has no attribute 'socre' print(s1.score)
类方法 @classmethod
类方法是描述类的行为的方法,类方法属于类
说明:
一、类方法须要用@classmethod装饰器定义
二、类方法至少有一个形参,第一个形参用于绑定类,约定写为‘cls’
三、类和该类的实例均可以调用类方法
四、类方法不能访问此类建立的实例的属性(只能访问类变量)
class Car: count =0 #类变量 @classmethod def getTotalCount(cls): '''此方法为类方法,第一个参数cls,表明调用此方法的类''' return cls.count @classmethod def updateCount(cls,number): cls.count += number print(Car.getTotalCount())#用类来调用类方法 #Car.count += 1 #面向对象思想不提倡直接操做属性 Car.updateCount(2) print(Car.getTotalCount()) c1 = Car() c1.updateCount(100)#类的实例能够访问类方法,是把c1.__class__传给cls print(c1.getTotalCount())
问题:
一、类方法属于类
二、实例方法属于该类的实例
三、类内能不能有函数,既不属于类又不属于类的实例?
静态方法 @staticmethos
静态方法不属于类,也不属于类的实例,它至关于定义在类内的普通函数,只是它的做用域属于类
class A: @staticmethod def myadd(x,y): '''此方法为静态方法,此方法的形参不须要传入类或实例''' return x+y print("1+2=",A.myadd(1,2)) a = A() print("100+200=",a.myadd(100,200)) print(myadd(10,20))#NameError: name 'myadd' is not defined
继承 inheritance 和 派生 derived
什么是继承/派生
一、继承是指从已有的类中衍生出新类,新类具备原有类的行为和属性,并能扩展新的行为和属性
二、派生就是从一个已有类中衍生(建立)新类,在新类上能够添加新的属性和行为
继承和派生的目的:
一、继承是延续旧类的功能
二、派生是为了在旧类的基础上添加新的功能
做用:
一、用继承派生机制,能够将一些共有功能加在基类中,实现代码的共享
二、在不改变基类的基础上改变原有的功能
继承/派生的名词:
基类(base class),超类(super class),父类(father class)
派生类(derived class),子类(child class)
单继承:
语法:class 类名( 基类名):
语句块
说明:单继承是指派生类由一个基类衍生出来的类
继承说明:
一、任何类都直接或间接的继承自object类
二、object类是一切类的超类(祖类)
类的__base__属性:
__base__属性用来记录此类的基类
说明:类名.__base__ 返回的是继承的第一个类
覆盖 override:
什么是覆盖:
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类实例调用该方法时,实例调用的是子类中的覆盖版本的方法,这种现象叫作覆盖
问题:当覆盖发生时,子类对象可否访问父类中的方法?
子类对象显式调用基类方法的方式:
基类名.方法名(实例,实际调用传参)
super 函数:
super(type,obj) 返回绑定超类的实例
super() 返回绑定超类的实例,等同于:super(__class__,实例方法的第一个参数,必须在方法内调用)
def work(self): print("A.walk()被调用") class B(A): def work(self): print("B.walk()被调用") def super_work(self): #此处能调用超类的work方法 #super(B,self).work() #A.walk() #super(__class__,self).work() #A.walk()被调用,在一个类内有一个__class__属性, super().work() #A.walk(),在一个实例方法中__class__和self 都是已知的能够省略 b = B() b.work() # B.walk()被调用 b.__class__.__base__.work(b) #A.walk()被调用,使用显式的方法调用超类的方法 super(B,b).work()#A.walk()被调用,super(B,b)返回的是B类的超类的对象实例 b.super_work()
显示调用基类的初始化方法:
当子类中实现了__init__方法时,基类的__init__方法并不会被调用,此时须要显式调用
class Human(): def __init__(self,n,a): self.name = n self.age = a def infos(self): print("姓名:",self.name) print("年龄",self.age) class Student(Human): def __init__(self,n,a,s): super().__init__(n,a)#使用超类中的__init__方法给name,age赋值 self.score = s s1 = Student("小张",20,100) s1.infos()
用于类的函数:
issubclass(cls,class_or_tuple):
判断一个类是否继承自其它的类,若是此类cls是class或tuple中的一个派生子类则返回True,不然返回False
说明:cls是class的子类的子类...也返回True
查看python内建类的继承关系的方法:
help(__bulitins__)
封装 enclosure
一、封装是指隐藏类的实现细节,让使用者不用关心这些细节
二、封装的目的是让使用者尽量少的使用实例变量(属性)进行操做
私有属性:
python类中,以双下划线“__”开头的,不以双下划线结尾的标识符为私有成员,在类的外部没法直接访问
class A: def __init__(self): self.__p1 = 100#__p1为私有属性,在类的外部不能调用 def test(self): print(self.__p1)#在类内能够调用私有属性 self.__m1() #A类的方法能够调用私有方法 def __m1(self): '''我是私有方法,只有我本身的类的方法才能调用我''' print("我是A类的__m1方法") a = A() #print(a.__p1)#在类外看不到__p1属性,访问失败 a.test() #a.__m1() #出错,没法调用私有方法
多态 polymorphic
字面意思:“多种状态”
多态是指在继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖版本方法的现象叫多态
说明:
多态调用的方法与对象相关,不与类型相关
Python的所有对象都只有“运行时状态(动态)”,没有“C++/Java”里的“编译时状态(静态)”
class Shape(): def draw(self): print("Shape.draw被调用") class Point(Shape): def draw(self): print("正在画点") class Circle(Point): def draw(self): print("正在画圆") def my_draw(s): s.draw() # 此处会调用谁的方法,此处显示多态中的动态 s1 = Circle() s2 = Point() s3 = Shape() my_draw(s1)#正在画圆 my_draw(s2)#正在画点 my_draw(s3)#Shape.draw被调用
面向对象的编程语言的特征:
继承
封装
多态
如:C++(支持多继承)/ Java /Python(支持多继承)/Swift /C#
多继承 multiple inheritance
多继承是指一个子类继承自两个或两个以上的基类
语法:
class 类名(基类名1,基类名2....):
语句块
说明:
一、一个子类同时继承自多个父类,父类中的方法能够同时被继承下来
二、若是两个父类中有同名的方法,而在子类中又没有覆盖此方法时,调用结果难以肯定
多继承的问题(缺陷):
标识符(名字空间冲突的问题)
要谨慎使用多继承
class Car: def run(self,speed): print("汽车以",speed,"千米/小时的速度行驶") class Plane: def fly(self,height): print("飞机以海拔",height,"的高度飞行") class PlaneCar(Car,Plane): pass p1 = PlaneCar() p1.fly(10000) p1.run(300)
多继承的MRO(Method Resolution Order)问题:
类内的__mro__属性用来记录继承方法的查找顺序
class A: def m(self): print("A.m()被调用") class B: def m(self): print("B.m()被调用") class AB(A,B): pass ab = AB()#多继承的调用方法,从左至右,,能够根据AB.__mro__来查看继承的顺序 ab.m()
class A: def m(self): print("A.m()") class B(A): def m(self): print("B.m()") class C(A): def m(self): print("C.m()") class D(B,C): def m(self): print("D.m()") d = D() print(D.__mro__)#(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) d.m() ############################################################################################################################################ class A: def m(self): print("A.m()") class B(A): def m(self): print("B.m()") super().m()# 此处调用的是C类的方法,在多继承中super()的窒执行顺序是按照__mro__的顺序来查找的 class C(A): def m(self): print("C.m()") class D(B,C): def m(self): print("D.m()") super().m()#此处调用B类的m方法 d = D() d.m() print(D.__mro__) # D.m() # B.m() # C.m() # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
函数重写 override:
重写是在自定义的类内添加相应(內建函数的方法)的方法,让自定义的类生成的对象(实例)像内建对象同样进行內建的函数操做
class MyNumber: pass n1 = MyNumber() x = len(n1) print(x) #TypeError: object of type 'MyNumber' has no len() ################################################################################### class MyNumber: def __len__(self):#重写了len方法,重写的必须是内建函数的方法 return 10 n1 = MyNumber() x = len(n1) #至关于x = n1.__len__() print(x) #10
对象转化为字符串函数重写
repr(obj)返回一个表明此对象的表达式字符串,一般:eval(repr(obj)) == obj
str(obj)经过给定的对象返回一个字符串(这个字符串一般是给人看的)
class MyNumber: def __init__(self,value): self.data = value n1 = MyNumber(100) print(str(n1))#<__main__.MyNumber object at 0x7f676201cfd0>,至关于调用n1.__str__(self) print(repr(n1))#<__main__.MyNumber object at 0x7f676201cfd0>,至关于调用n1.__repr__(self) ######################################################################################### class MyNumber: def __init__(self,value): self.data = value def __str__(self): return "数字:%d" % self.data def __repr__(self): return "MyNumber(%d)"% self.data n1 = MyNumber(100) print(str(n1))#数字:100,至关于调用n1.__str__(self) print(repr(n1))#MyNumber(100),至关于调用n1.__repr__(self) #n2 = eval(str(n1))#SyntaxError: invalid character in identifier n2 = eval(repr(n1))#能够建立n2对象实例
对象转字符串函数的重写方法:
repr()函数的重写方法:
def __repr__(self):
return 可以表达self内容的字符串(能够经过eval()方法反推)
str()函数的重写方法:
def __str__(self):
return 人能看懂的字符串
说明:
一、str(obj)函数优先调用obj.__str__()方法返回字符串
二、若是obj没有__str__()方法,则调用obj.__repr__()方法返回的字符串
三、若是obj没有__repr__()方法,则调用object类的__repr__()实例方法显示<XXXXX>格式的字符串
四、print(对象)至关于print(str(对象)),一个对象(实例)就是首先寻找__str__方法,在寻找__repr__方法
数值转换函数的重写:
def __complex__(self) complex(obj)函数调用
def __int__(self) int(obj)函数调用
def __float__(self) float(obj)函数调用
def __bool__(self) bool(obj)函数调用
class MyNumber: def __init__(self,value): self.data = value def __repr__(self): print("__repr__被调用") return "MyNumber(%d)"% self.data def __int__(self): '''此方法int(obj)函数重载,必须返回整数,此方法一般用于制定自定义对象如何转为整数的规则''' return self.data n1 = MyNumber(100) print(type(n1))#<class '__main__.MyNumber'> n = int(n1) #至关于n1.__int__(self) print(type(n))#<class 'int'> print(n)
內建函数的重写:
__abs__ abs(obj)
__len__ len(obj)
__reversed__ reversed(obj)
__round__ round(obj)
'''自定义一个MyList类,与系统內建的类同样,用来保存有前后顺序关系的数据''' class MyList(): def __init__(self,iterator=[]): self.data = [x for x in iterator] def __repr__(self): return "MyList(%r)" % self.data def __abs__(self): #return MyList([abs(x) for x in self.data]) #上一句语句能够用以下生成器表达式代替以防止过多的占用内存 return MyList((abs(x) for x in self.data)) def __len__(self): return len(self.data) myl = MyList([1,-2,3,-4]) print(myl)#MyList([1, -2, 3, -4]) print(abs(myl))#MyList([1, 2, 3, 4]) print("原来的列表:",myl)#原来的列表: MyList([1, -2, 3, -4]),原来的列表 myl2 = MyList(range(10)) print("myl2的长度是:",len(myl2))#10 print("myl的长度是:",len(myl)) #4
布尔测试函数的重写:
格式:def __bool__(self):
....
做用:
一、用于bool(obj)函数取值
二、用于if语句真值表达式中
三、用于while语句真值表达式中
说明:
一、优先调用__bool__方法取值
二、若是不存在__bool__方法,则用__len__()方法取值,后判断是否为零值,若是不为零返回True,不然返回False
三、若是再没有__len__方法,则直接返回True
class MyList(): def __init__(self,iterator=[]): self.data = [x for x in iterator] def __repr__(self): return "MyList(%r)" % self.data def __abs__(self): #return MyList([abs(x) for x in self.data]) #上一句语句能够用以下生成器表达式代替以防止过多的占用内存 return MyList((abs(x) for x in self.data)) def __len__(self): print("__len__方法被调用") return len(self.data) myl = MyList([1,-2,3,-4]) myl2 = MyList([]) print(bool(myl))# True print(bool(myl2))# False if myl2:#在此处会查找__bool__,若是没有再查找__len__,再没有直接返回True pass while myl2:#在此处会查找__bool__,若是没有再查找__len__,再没有直接返回True pass
迭代器(高级):
什么是迭代器
能够经过next(it)函数取值的对象就是迭代器
迭代器协议:
迭代器协议是指对象可以使用next函数获取下一项数据,在没有下一项数据是触发StopIterator来终止迭代的约定
实现方法:
类内须要有__next__(self)方法来实现迭代器协议
语法形式:
class MyIterator:
def __next__(self):
迭代器协议的实现
return 数据
什么是可迭代对象:
一、是指能用iter(obj)函数返回迭代器的对象(实例)
二、可迭代对象内部必定要定义__iter__(self)方法来返回迭代器
可迭代对象的语法形式:
class MyIterable:
def __iter__(self):
语句块
return 迭代器
class MyList: def __init__(self,iterator): '''自定义列表的初始方法,此方法建立一个data实例变量来绑定一个用来存储数据的列表''' self.data = list(iterator) def __repr__(self): '''此方法为了打印此列表的数据''' return 'MyList(%r)' % self.data def __iter__(self): '''有此方法就是可迭代对象,但要求必须返回迭代器''' print("__iter__方法被调用") return MyListIterator(self.data) class MyListIterator: '''此类用来建立一个迭代器对象,用此迭代器对象能够迭代访问MyList类型的数据''' def __init__(self,iter_data): self.cur = 0 #设置迭代器的初始值为0表明列表下标 self.iter_data = iter_data def __next__(self): '''有此方法的对象才叫迭代器',此方法必定要实现迭代器协议''' print("__next__方法被调用") if self.cur >= len(self.iter_data): raise StopIteration #不然还没有迭代完成,须要返回数据 r = self.iter_data[self.cur]# 拿到要送回去的数据 self.cur +=1 #将当前值向后移动一个单位 return r myl = MyList([2,3,5,7]) print(myl) for x in myl: print(x)
对象的属性管理函数:
一、getattr(obj,name[,dafault]) 从一个对象获得对象的属性;gettattr(x,'y')等同于x.y;当属性y不存在时,若是有default参数,则返回default,若是没有给出default则产生一 个AttributeError错误
二、hasattr(obj,name) 用给定的name返回obj是否有属性,若是有返回True,不然返回False,此种作法能够避免在getattr(obj,name)时引起错误
三、setattr(obj,name,value) 给对象obj的名为name的属性设置相应的值value,setattr(x,'y',v)等同于x.y = v
四、delattr(obj,name) 删除对象obj中的name属性,delattr(x,'y')等同于del x.y
class Car: def __init__(self,c,b): self.color = c self.brand = b def get_car_attr(self,attr_name): '''此方法用于获取对象的属性,若是属性在次对此对象中返回该属性,若是不在时返回None''' return getattr(self,attr_name,None) c1 =Car("黑色",'Benz') v = c1.get_car_attr('color') if v is None: print("没有颜色属性") else: print("颜色是",v)
运算符重载:
什么是运算符重载
让自定义的类生成的对象(实例)可以使用运算符进行操做
做用:
一、让自定义的类的实例像內建对象同样运行运算符操做
二、让程序简洁易读
三、对自定义的对象,将运算符赋予新的运算规则
算术运算符的重载:
__add__(self, rhs) self + rhs 加法
__sub__(self, rhs) self - rhs 减法
__mul__(self, rhs) self * rhs 乘法
__truediv__(self, rhs) self / rhs 除法
__floordiv__(self, rhs) self // rhs 地板出法
__mod__(self, rhs) self % rhs 求余
__pow__(self, rhs) self ** rhs 幂运算
注意:rhs(right hands side )右手边
class MyNumber: def __init__(self,v): self.data = v def __repr__(self): return "MyNumber(%)" % self.data # def myadd(self,other): # v = self.data + other.data # return MyNumber(v) def __add__(self,other): v = self.data + other.data return MyNumber(v) def __sub__(self,other): v = self.data - other.data return MyNumber(v) n1 = MyNumber(100) n2 = MyNumber(200) #n3 = n1.myadd(n2) n3 = n1 + n2 # 等同于n1.__add__(n2) print(n3)#MyNumber(300) n4 = n3 - n2 #等同于n3.__sub__(n2) print(n4) #MyNumber(100)
反向算数运算符的重载:
当左手边的类型为內建类型,右手边为自定义类型时,要实现运算必须用如下方法重载
__radd__(self, lhs) lhs + self 加法
__rsub__(self, lhs) lhs - self 减法
__rmul__(self, lhs) lhs * self 乘法
__rtruediv__(self, lhs) lhs / self 除法
__rfloordiv__(self, lhs) lhs // self 地板出法
__rmod__(self, lhs) lhs % self 求余
__rpow__(self, lhs) lhs ** self 幂运算
'''实现两个自定义列表相加''' class MyList(): def __init__(self,iterable): self.data = iterable def __repr__(self): return 'MyList(%s)' % self.data def __mul__(self,rhs): v = self.data * rhs return MyList(v) def __add__(self,other): v = self.data +other.data return MyList(v) def __rmul__(self,lhs): return MyList(self.data*lhs) def __iadd__(self,other): self.data.extend(other.data)#此出是改变self自己,返回值为None return self L1 = MyList([1,2,3]) L2 = MyList([4,5,6]) L3 = L1 + L2 #等同于L1.__add__(L2) print(L3)#MyList([1, 2, 3, 4, 5, 6]) L4 = L2 + L1 print(L4)#MyList([4, 5, 6, 1, 2, 3]) L5 = L1 * 2 #等同于L1.__mul__(2) print(L5)#MyList([1, 2, 3, 1, 2, 3]) #(反方算数运算符)当左手边的类型为內建类型,右手边为自定义类型时,要实现运算必须用一下方法重载 L6 = 2* L1 print(L6)#MyList([1, 2, 3, 1, 2, 3]) #符合算数运算符: L1 += L2 #当没有__iadd__方法时,等同于调用L1 = L1 + L2,在这里又从新建立了一个MyList类的对象 print(L1)#当有__iadd__方法时,在这里不会从新建立了一个MyList类的对象,而是在L1实例上改变
符合赋值运算符的重载:
__iadd__(self, rhs) self += rhs 加法
__isub__(self, rhs) self -= rhs 减法
__imul__(self, rhs) self *= rhs 乘法
__itruediv__(self, rhs) self /= rhs 除法
__ifloordiv__(self, rhs) self //= rhs 地板出法
__imod__(self, rhs) self %= rhs 求余
__ipow__(self, rhs) self **= rhs 幂运算
比较运算符的重载:
__lt__(self,rhs) self < rhs 小于
__le__(self,rhs) self <= rhs 小于等于
__gt__(self,rhs) self > rhs 大于
__ge__(self,rhs) self >= rhs 大于等于
__eq__(self,rhs) self == rhs 等于
__ne__(self,rhs) self != rhs 不等于
注:比较运算符一般返回True 或 False
lt:less than le:less or equal
gt:greater than gt:gerater or equl
eq:equal ne:not equal
位运算符重载:
__invert__(self) ~ self 取反(一元运算符)
__and__(self,rhs) self & rhs 位与
__or__(self,rhs) self | rhs 位或
__xor__(self,rhs) self ^ rhs 位异或
__lshift__(self,rhs) self << rhs 左移
__rshift__(self,rhs) self >> rhs 右移
反向位运算符重载:
__rand__(self,lhs) lhs & self 位与
__ror__(self,lhs) lhs | self 位或
__rxor__(self,lhs) lhs ^ self 位异或
__rlshift__(self,lhs) lhs << self 左移
__rrshift__(self,lhs) lhs >> self 右移
注:当左手边的类型为內建类型,右手边为自定义类型时,使用反向位运算符重载
复合赋值位运算符:
__iand__(self,lhs) lhs &= self 位与
__ior__(self,lhs) lhs |= self 位或
__ixor__(self,lhs) lhs ^= self 位异或
__ilshift__(self,lhs) lhs <<= self 左移
__irshift__(self,lhs) lhs >>= self 右移
一元运算符的重载:
__neg__(self) - self 负号
__pos__(self) + self 正号
__invert__(self) ~ self 取反
一元运算符的重载方法:
class 类名:
def __XXX__(self):
....
class MyList(): def __init__(self,iterable): print("__init__被调用") self.data = iterable def __repr__(self): return 'MyList(%r)' % self.data def __neg__(self): '''此方法用来制定 - self 返回的规则''' L = (-x for x in self.data) return MyList(L) L1 = MyList([1,-2,3,-4]) L2 = -L1 print(L2)#MyList([-1, 2, -3, 4])
运算符重载说明:
运算符重载不能改变运算符的优先级
python类名最好用驼峰命名法:
MyList MyRange 大驼峰(全部单词首字母大写,其他小写)
getStudentAge 小驼峰(第一个单词首字母小写,其余首字母大写)
in /not in 运算符的重载:
重载方法:
def __contanins__(self,e) e in self 成员运算
class MyList(): def __init__(self,iterable): self.data = iterable def __repr__(self): return 'MyList(%r)' % self.data def __contains__(self,e): '''此方法用来实现 in /not in 运算符的重载''' print("__contains__被调用") for x in self.data: if x == e: return True return False L1 = MyList([1,-2,3,-4]) if -2 in L1: print("-2在L1中") else: print("-2不在L中") #当MyList的类内重载了__contains__方法,则not in 也同时可使用(将__contains__方法的返回值取反) if 5 not in L1: print("5不在L1中") else: print("-5在L中")
索引和切片运算符的重载:
__getitem__(self,i) x = self[i] 索引和切片取值
__setitem__(self,i,v) self[i] = v 索引和切片赋值
__delitem__(self,i) del self[i] del 语句删除索引
做用:让本身定义的类型的对象可以支持索引和切片操做
slice函数:
做用:用于建立一个Slice切片对象,此对象存储一个切片的起始值,终止值和步长信息
slice(start,stop=None,step=None) 建立一个切片对象
slice的对象的属性:
s.start 切片的起始值,默认为None
s.stop 切片的终止值,默认为None
s.step 切片的步长,默认为None
class MyList(): def __init__(self,iterable): print("__init__被调用") self.data = iterable def __repr__(self): return 'MyList(%r)' % self.data def __getitem__(self,i): print("__getitem__被调用,i=",i) if type(i) is not int: raise TpyeError return self.data[i] def __setitem__(self,i,v): self.data[i] = v L1 = MyList([1,-2,3,-4]) v = L1[1] print(v)#-2 L1[1] = 2 print(L1)#MyList([1, 2, 3, -4])
class MyList(): def __init__(self,iterable): print("__init__被调用") self.data = iterable def __repr__(self): return 'MyList(%r)' % self.data def __getitem__(self,i): print("__getitem__被调用,i=",i) if type(i) is int: print("正在作索引操做") elif type(i) is slice: print("正在作切片操做") print("起始值:",i.start) print("终止值:",i.stop) print("步长:",i.step) else: raise KeyError return self.data[i] L1 = MyList([1,-2,3,-4,5,-6]) print(L1[::2])#等同于调用L1[slice(None,None,2)] # __init__被调用 # __getitem__被调用,i= slice(None, None, 2) # 正在作切片操做 # 起始值: None # 终止值: None # 步长: 2 # [1, 3, 5]
类的特殊成员
一、__call__()方法
class Foo: def __init__(self): print("init") def __call__(self): print("call") Foo()()#首先会执行__init__()方法,再执行__call__()方法 obj = Foo()#执行__init__()方法 obj()#执行__call__()方法
metaclass(类的祖宗)
一、Python中一切事物都是对象
二、class Foo:
pass
obj = Foo()
#obj是对象,Foo类的对象
#Foo类也是一个对象,type的对象
三、类都是type类的对象,type(...)
def function(self): print("hello world") #建立一个Foo类继承object,含有func()方法 Foo = type("Foo",(object,),{'func':function}) obj = Foo() obj.func()
class MyType(type): def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs)#执行父类type的__init__方法 print("MyType的__init__") def __call__(self): print("MyType的__call__") obj = self.__new__(self)#self表明Foo,第三步调用Foo中的new方法,返回Foo的对象 self.__init__(obj)#第四步调用Foo中的__init__方法 class Foo(object,metaclass=MyType):#第一步,编译器执行到此处是会调用MyType的__init__方法 def __init__(self): print("Foo的__init__") def __new__(cls,*args,**kwargs): print("Foo中的__new__方法") return object.__new__(cls,*args,**kwargs)#真正建立Foo类的对象的地方 obj = Foo()#第二步,在Foo类(MyTpye类的对象)加(),会执行MyType中的__call__方法