Python-15(类与对象)
OOP编程即面向对象编程。
面向对象是一种代码封装的方法。一方面从静态的属性特性入手,另一方面从行为能力入手。
对象 = 属性+方法
生产对象之前必须先编写一个类。
类的首字母需大写(约定俗成)。
属性就是写在类里的变量,方法就是写在类里的函数。
封装
在创建对象之前,通过类将性格的属性方法打包到一起,然后通过类生产相应的对象。
class People:head = 1eyes = 2legs = 2hand = 2def say(self):print("good morning")def eat(self):print("正在吃饭")
p1 = People()
p2 = People()
p2.hand = 1
print(p1.hand)
print(p2.hand)
p1.say()
p2.eat()
注意:不同对象之间是不会共享数据的,更改p2的属性,p1的属性不会随之改变。
可以增加对象的属性。
class People:head = 1eyes = 2legs = 2hand = 2def say(self):print("good morning")def eat(self):print("正在吃饭")
p1 = People()
p2 = People()
p1.mouth = 1
print(dir(p1))
print(dir(p2))
self是实例对象本身,self参数传递的信息让python明白是哪个对象在调用函数,所有类中的方法默认的参数都是self,第一个参数也是self。
class People:def say(self):print(self)
p1 = People()
p1.say()
print(p1)
继承
python的类是支持继承的,可以使用现有类的所有功能,并在无需重新编写代码的情况下,对这些功能进行扩展。通过继承创建的新类我们称之为子类,而被继承的类我们称之为父类、基类或者超类。
class Animal:category = 100def say(self):print("我是动物")
class Dog(Animal):category = 20def say(self):print("我是狗")
a = Animal()
d = Dog()
a.say()
d.say()
注意:子类的属性值和方法会覆盖父类的属性值和方法。
isinstance()函数
用于判断一个对象是否属于某个类。
class Animal:category = 100def say(self):print("我是动物")
class Dog(Animal):category = 20def say(self):print("我是狗")
a = Animal()
d = Dog()
print(isinstance(d,Dog))
print(isinstance(d,Animal))
print(isinstance(a,Dog))
issubclass()函数
用于检测一个类是否为某个类的子类。
class Animal:category = 100def say(self):print("我是动物")
class Dog(Animal):category = 20def say(self):print("我是狗")
a = Animal()
d = Dog()
print(issubclass(Dog,Animal))
print(issubclass(Animal,Dog))
多重继承
一个子类同时可以继承多个父类。
class Father:sex = "男"def say(self):print("我是父亲")
class Monther:sex = "女"def say(self):print("我是母亲")
class Children(Father,Monther):def say(self):print("我是孩子")
c = Children()
print(c.sex)
注意:多重继承的顺序自左向右,只有在当前类中找不到所需的属性才会向下一个父类(右边的)进行寻找。
组合
当类于类的关系不是从属关系,而是并列的关系,这时使用组合,即将相关的实例放到某个类中。
class Father:sex = "男"def say(self):print("我是父亲")
class Monther:sex = "女"def say(self):print("我是母亲")
class Children:def say(self):print("我是孩子")
class Family:f = Father()m = Monther()c = Children()def say(self):self.f.say()self.m.say()self.c.say()
fm = Family()
fm.say()
绑定(self)
self的作用就是将实例对象跟类的方法进行绑定,在实例中除了类的方法,实例的属性却可以是自己。python是一门支持内省的编程语言。其对象在运行的时候拥有自我观察的能力。
__dict__
显示当前对象拥有的私有属性,以字典的形式来保存其属性和其对应的值。
class Father:year = 30def say(self):print("我是父亲")
f = Father()
f.sex = "男"
print(f.__dict__)
注意:在类中定义的公共属性无法显示。
定义属性
可以使用self,利用类中的方法来定义对象自己的属性。self就是实例化对象本身。
class Father:def set_year(self,y):self.year = ydef set_name(self,n):self.name = n
f = Father()
f.set_year(30)
f.set_name("王强")
print(f.__dict__)
注意:如果要修改对象的属性,必须通过self绑定。
构造函数
构造函数即__init__(),只需要在类中定义__init__()方法,就可以在实例化对象的同时实现个性化定制。
class Number:def __init__(self,x,y):self.x = xself.y = ydef add(self):return self.x+self.ydef mul(self):return self.x*self.y
n = Number(3,4)
print(n.add())
print(n.mul())
print(n.__dict__)
注意:等号左边的绑定到实例化对象里的属性,等号右边是传进来的参数。
重写
当父类的方法不能满足子类的需求,我们可以对父类方法重写,其指子类中的方法名与父类方法名称相同,但参数列表和内部代码可以不同。相当于子类对父类的方法进行重新定义。
class Number:def __init__(self,x,y):self.x = xself.y = ydef add(self):return self.x+self.ydef mul(self):return self.x*self.yclass Number2(Number):def __init__(self,x,y,z):Number.__init__(self,x,y)self.z = zdef add(self):return Number.add(self) + self.zdef mul(self):return Number.mul(self) * self.z
n = Number2(3,4,5)
print(n.add())
print(n.mul())
print(n.__dict__)
调用未绑定的父类方法,即直接通过类名访问类里面的方法。但可能造成钻石继承。
钻石继承
类A的构造方法会被重复调用两次。
class A:def __init__(self):print("我是A")
class B1(A):def __init__(self):A.__init__(self)print("我是B1")
class B2(A):def __init__(self):A.__init__(self)print("我是B2")
class C(B1,B2):def __init__(self):B1.__init__(self)B2.__init__(self)print("我是C")
c = C()
super()函数
super函数能够解决钻石继承的问题,其在父类中搜索指定的方法,并自动绑定后self参数。使用super函数去查找父类的方法,它会自动按照MRO顺序(是Python中用于确定多继承场景下方法调用顺序的算法)去搜索父类的相关方法。
class A:def __init__(self):print("我是A")
class B1(A):def __init__(self):super().__init__()print("我是B1")
class B2(A):def __init__(self):super().__init__()print("我是B2")
class C(B1,B2):def __init__(self):super().__init__()print("我是C")
c = C()
mro()方法
可以使用mro方法来查找一个类的MRO。
class A:def __init__(self):print("我是A")
class B1(A):def __init__(self):super().__init__()print("我是B1")
class B2(A):def __init__(self):super().__init__()print("我是B2")
class C(B1,B2):def __init__(self):super().__init__()print("我是C")
c = C()
print(C.mro())
注意:其中的object类是所有类的基类,就算不写也会被隐式的继承。
__mro__属性
其功能与mro方法类似。
class A:def __init__(self):print("我是A")
class B1(A):def __init__(self):super().__init__()print("我是B1")
class B2(A):def __init__(self):super().__init__()print("我是B2")
class C(B1,B2):def __init__(self):super().__init__()print("我是C")
print(C.mro())
print(B1.__mro__)
Mixin(Mix-in 混入或乱入)
即后续增添的功能。
多态
多态是指同一个运算符、函数或者对象在不同场景下具有不同作用效果。
运算符的多态
print(3+5)
print(3*5)
print("py"+"thon")
print("py"*3)
len()函数
print(len("python"))
print(len(["","",""]))
print(len({"name":"小强","age":18}))
类的多态
重写就是显示类继承的多态,python允许在子类中定义和父类同名的方法,如果对父类的某个方法不满意,可以在子类中重新定义一个同名的方法进行覆盖。
正方形类和圆形类都继承于Sharp类,但是他们都重写了构造函数和area()方法,这就是多态的体现。
class Sharp:def __init__(self,name):self.name = namedef area(self):pass
class Square(Sharp):def __init__(self,length):super().__init__("正方形")self.length = lengthdef area(self):return self.length * self.length
class Circle(Sharp):def __init__(self,radius):super().__init__("圆形")self.radius = radiusdef area(self):return self.radius * self.radius * 3.14
s = Square(5)
print(s.area())
c = Circle(6)
print(c.area())
print(s.name)
print(c.name)
自定义函数实现多态接口
传入函数的参数不同,所产生的效果也不同。
class Cat:def __init__(self,name,age):self.name = nameself.age = agedef intro(self):print(f"我是一只猫,我叫{self.name},今年{self.age}岁")def say(self):print("喵喵喵")
class Dog:def __init__(self,name,age):self.name = nameself.age = agedef intro(self):print(f"我是一只狗,我叫{self.name},今年{self.age}岁")def say(self):print("汪汪汪")
class Pig:def __init__(self,name,age):self.name = nameself.age = agedef intro(self):print(f"我是一只猪,我叫{self.name},今年{self.age}岁")def say(self):print("哼哼哼")
c = Cat("小喵",3)
d = Dog("小汪",4)
p = Pig("小胖",5)
def animal(x):x.intro()x.say()
animal(c)
animal(d)
animal(p)
私有变量
通过某种手段,使得对象中的属性或方法无法被外部所访问。在python中,进行从一个对象内部才能够进行访问的”私有变量“并不存在。
name mangling(名字改编、名称改写或名称修饰)
语法是在名字的前面加上两个连续的下划线(__)。以两个下划线开头的变量代表是私有变量。
无法直接通过实例对象来访问或更改其私有变量,只能通过其内部函数进行访问或修改。
class C:def __init__(self,x):self.__x = xdef set_x(self,x):self.__x = xdef get_x(self):print(self.__x)
c = C(520)
c.set_x(1314)
c.get_x()
注意:python的私有方式只是将程序员想要私有的变量改个名藏起来,我们可以通过以下方式进行访问(但不建议这么做):
_类名__变量名(方法名)
class C:def __init__(self,x):self.__x = xdef set_x(self,x):self.__x = xdef get_x(self):print(self.__x)
c = C(520)
print(c._C__x)
注意:在类实例化对象之后,手动添加私有变量,其名字无法被改编。名字改编是发生在类实例化对象时的事情。
class C:def __init__(self,x):self.__x = xdef set_x(self,x):self.__x = xdef get_x(self):print(self.__x)
c = C(520)
c.__y = 1314
print(c.__dict__)
单个下划线开头的变量(_变量名)是仅供内部使用的变量。
单个下划线结尾的变量(变量名_)是将python使用的关键字二次使用,防止报错。
__slots__(节省空间)
对于一个明确自己需要多少属性,也不需要动态的添加属的类来说,那么利用字典来存放属性的方式非常浪费空间。使用__slots__类属性可以避免利用字典来存放造成空间上的浪费。
__slots__ = ["属性1","属性2"......]
其中的属性就是希望此类的实例化对象可以使用的属性名称。
class C:__slots__ = ["x","y"]def __init__(self,x):self.x = x
c = C(520)
c.y = 1314
print(c.x)
print(c.y)
c.z = 888
注意:继承自父类的__slots__属性是不会在子类中生效的,python只会关注各个具体的类中定义的__slots__属性。
class C:__slots__ = ["x","y"]def __init__(self,x):self.x = x
class D(C):pass
d = D(520)
d.y = 1314
d.z = 666
print(d.__slots__)
print(d.__dict__)
__new__(cls[,...])方法
在init方法被调用之前,new放就被调用了。事实上对象就是由new方法创建的。
重写new方法有两种情况,一种情况是在元类中去定制类,另一种情况是在继承不可变数据类型。
class CapStr(str):def __new__(cls, string):string = string.upper()return super().__new__(cls,string)
c = CapStr("python")
print(c)
之所以能够修改不可变对象,是因为在创建实例对象之前进行拦截,然后再调用super().__new__()去创建真正的实例。
CapStr继承自str类,那么也会继承str类的各种方法。
__del__()方法
只有对象被销毁时才会触发该方法。当检测到对象没有任何引用时,才会将其销毁。
对象的重生
只要在对象被销毁之前将对象送出去,那么理论上del语句就不会调用到__del__()方法。因为此时这个对象还存在引用。
使用全局变量
class D:def __init__(self,name):self.name = namedef __del__(self):global xx= self
d = D("python")
print(d)
print(d.name)
del d
print(d)
输出x发现x其实就是d
class D:def __init__(self,name):self.name = namedef __del__(self):global xx= self
d = D("python")
print(d)
print(d.name)
del d
print(x)
print(x.name)
使用函数调用
通过参数传递的形式将self传出去。
class D:def __init__(self,name,func):self.name = nameself.func = funcdef __del__(self):self.func(self)
def outter():x = 0def inner(y = None):nonlocal xif y:x = yelse:return xreturn inner
f = outter()
d = D("python",f)
print(d)
del d
g = f()
print(g)
f就是inner,当del d的时候执行inner(self),把self保存到x中,然后g = f(),相当于g = inner(),此时传入的参数为none,就把之前保存的x输出出来。
属性访问
hasattr(对象,属性)函数
判断该对象是否存在指定的属性。
class C:def __init__(self,name,age):self.name = nameself.__age = age
c = C("James",18)
print(hasattr(c,"name"))
getsttr(对象,属性名)函数
获取该对象的指定属性的值。
class C:def __init__(self,name,age):self.name = nameself.__age = age
c = C("James",18)
print(getattr(c,"name"))
注意:如果想获取私有变量的值,需要通过_类名__属性名的方式获取。
setattr(对象,属性,属性值)函数
在该对象中更改指定属性值。
class C:def __init__(self,name,age):self.name = nameself.__age = age
c = C("James",18)
setattr(c,"_C__age",19)
print(getattr(c,"_C__age"))
delattr(对象,属性)函数
删除该对象的指定属性。
class C:def __init__(self,name,age):self.name = nameself.__age = age
c = C("James",18)
delattr(c,"_C__age")
print(hasattr(c,"_C__age"))