其实析构方法就是对象删除销毁时,项目需要进行对应的提示和操作
class Animal:
def __init__(self,name):
self.name=name
print("这是构造初始化方法")
pass
def __del__(self):
print('当在某个作用域下面,没有被使用【引用】的情况下,解释器会自动的调用此函数,来释放内存空间')
print("这是析构方法")
pass
cat=Animal('小花猫')
我们可以看到,是先构造后析构,然后对象被清理后会自动执行析构函数
1、当整个程序脚本执行完毕后会自动调用__del__方法
2、当对像被手动销毁时也会自动调用 __del__ 方法
3、析构函数一般用于资源回收,利用__del__方法销毁对象回收内存等资源
在python中展现面向对象的三大特征:
封装、继承、多态
封装;指的是把内容封装到某个地方,便于后面的使用
他需要:把内容封装到某个地方,从另外一个地方去调用被封装的内容,对于封装来说,其实就是使用初始化构造方法将内容封装到对象中,然后通过对象直接或者self来获取封装的内容
继承:和现实生活当中的继承是一样的:也就是子可以继承父的内容【属性和行为】(爸爸有的,儿子都有,相反,儿子有的,爸爸不一定有)
在现实生活中,继承一般指的是子女继承父辈的财产。在面向对象中同样有继承,例如:
猫的方法:喵喵叫、吃、喝
狗的方法:汪汪叫、吃、喝
如果给猫和狗都创建一个类,那么猫和狗的所有方法都要写,如:
上面的代码中我们可以发现,吃,喝方法时一样的,但是写了两遍。
如果用继承的思想,我们可以这样:猫和狗都是动物(动物有吃,喝的方法)
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法
案例中子类没有写任何方法,但是直接调用eat方法没有报错,这说明子类继承了父类的方法。
总结:在定义子类时要继承父类,只需要类名后面的小括号()中写上父类的名字,那么父类的属性、方法,会被继承给子类。
class Animal:
def eat(self):
'''
子类共同的行为
吃
:return:
'''
print('吃饭了')
def drink(self):
'''
喝
:return:
'''
print('喝水了')
class Dog(Animal):#继承了Animal 父类,此时Dog就是子类
def wwj(self):
'''
子类独有的行为
:return:
'''
print('小狗汪汪叫')
pass
class Cat(Animal):#继承了Animal 父类,此时Cat就是子类
def mmj(self):
print('小猫喵喵叫')
d1=Dog()
d1.eat()#继承了父类的行为
print('*'*30)
c1=Cat()
c1.drink()
所以对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需要继承父类,而不必去一一实现,这样就可以极大的提高效率,减少代码的重复编写,精简代码的层级结构,便于拓展
class 类名(父类)
子类可以继承一个父类,那是否可以继承两个父类或多个呢?答案是肯定的,这就是python的多继承
C类可以继承A、B两个类, 可以将A,B中的方法继承过来,C拥有A,B的方法和属性
在C类中并没有写方法,只继承了A,B两个父类。C可以调用两个父类的方法
class shenxian:
def fly(self):
print('神仙会飞')
pass
class monkey:
def chitao(self):
print('猴子喜欢吃桃')
class sunwukong(shenxian,monkey):
pass
swk=sunwukong()
swk.fly()
swk.chitao()
输出结果:
如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
class D(object):
def eat(self):
print('D.eat')
pass
pass
class C(D):
def eat(self):
print('C.eat')
pass
pass
class B(D):
pass
class A(B,C):
pass
a=A()
#在执行eat()方法的查找顺序
#首先到A里面去查找,如果A中没有,则继续到B中查找,如果B中查找没有,到C中进行查找,
#如果C没有,就到D中查找,如果还是没有找到就报错
#采取的是广度优先查找
a.eat()
print(A.__mro__)#可以显示类的继承关系
看看下面的继承关系,Son类继承Father类,Father类并没有提供eat方法,但是父类又继承了Grandfather类。Son的对象调用eat方法可以正常执行,运行结果得出,Son类也继承了Granderfather类的方法。这就是继承的传递性
class GrandFather:
def eat(self):
print('吃的方法')
pass
pass
class Father(GrandFather):
pass
class Son(Father):
pass
son=Son()
son.eat()#此方法是从GrandFather基础过来的
print(Son.__mro__)
输出结果:
总结:类的传递过程中,我们把父类又称为基类,子类又称为派生类,父类的属性和方法可以一级一级的传递到子类
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法 伪代码示例:
为什么要重写:父类的方法已经不能满足子类的需要,子类就可以重写父类的方法
class Dog:
def bark(self):
print('汪汪叫.....')
class keji(Dog):
def bark(self):
print('柯基叫')
kj=keji()
kj.bark()
如果在子类中有一个方法需要父类的功能,并且又要添加新的功能。如果直接重写父类方法,那么就要重复写很多代码。那么这就要调用父类方法
class Dog:
def __init__(self,name,color):
self.name=name
self.color=color
def bark(self):
print('汪汪叫.....')
class keji(Dog):
def __init__(self,name,color):#属于重写父类的方法
#我们需要去调用父类的构造函数
Dog.__init__(self,name,color)#显示调用父类的构造函数,就可以子类具备父类的实例属性
#扩展其他的属性
super(keji, self).__init__(name,color)#super是自动找到父类的调用方法
#假设有多个父类,会按照广度优先查找,找到了就调用
self.height=90
self.weight=20
pass
def __str__(self):
return '{}的颜色是{},身高是{}cm,体重是{}kg'.format(self.name,self.color,self.height,self.weight)
def bark(self):#属于重写父类的方法
super(keji, self).bark()#调用父类方法
# 假设有多个父类,会按照广度优先查找,找到了就调用
print('柯基叫')
kj=keji('柯基','黄色')
kj.bark()
print(kj)
结果:
类属性:就是类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象可以访问
实例属性:实例对象所拥有的属性,只能通过实例对象访问
#属性和实例属性
#类属性 就是类对象所拥有的属性
class Student:
name="李明"#属于类属性,就是student类对象所拥有的
def __init__(self,age):
self.age=age
pass
pass
lm=Student(18)
print(lm.name)#通过实例对象去访问类属性
print(lm.age)
#类属性是可以被类对象和实例对象共同访问使用
#实例属性只能由实例对象来访问
#顺序:先查找实例属性中查找->类属性查找
类属性类对象可以访问,实例对象也可以访问,这与内存中保存的方式有关
上图中可以看出,所有实例对象的类对象指针指向同一类对象。实例属性在每个实例中独有一份,而类属性是所有实例对象共有一份
要想修改类属性,采用类名.类属性进行修改
类对象所拥有的方法,需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,类方法可以通过类对象,实例对象调用
类方法主要可以对类属性进行访问、修改
class People:
country='China'
@classmethod #加上修饰后,下面的这个方法就成为了类方法
def get_country(cls):
return cls.country #访问类属性
pass
@classmethod
def change_country(cls,data):
cls.country=data #在类方法中修改类属性的值
pass
print(People.get_country())#通过类对象直接引用
p1=People()
print('实例对象访问%s'%p1.get_country())
print('***'*10)
People.change_country('英国')
print(People.get_country())#通过类对象直接引用
类对象所拥有的方法,需要用@staticmethod来表示静态方法,静态方法不需要任何参数,如:
#为什么要使用静态方法?
#由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互,
# 也就是说,在静态方法中不会涉及到类中方法和属性的操作
#数据资源能够得到有效的充分利用
class People:
country='China'
@staticmethod
def getData():
return People.country
print(People.getData())
p=People()
print(p.getData())
#注意,一般情况下,我们不会通过实例对象去访问静态方法
#demo 返回当前的系统时间
import time #引入时间模块
class TimeTest:
def __init__(self,hour,min,second):
self.hour=hour
self.min=min
self.second=second
@staticmethod
def shoeTime():
return time.strftime("%H:%M:%S",time.localtime())
print(TimeTest.shoeTime())
类方法的第一个参数是类对象cls,通过cls引用的类对象的属性和方法,必须用装饰器【@classmethod】
实例方法的第一个参数是实例对象self,通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象【或者实例对象】来引用。必须用装饰器【@staticmethod】
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,Python崇尚“鸭子类型”,利用python伪代码实现Java和C#的多态
【就是多种状态,多种形态,就是同一种行为对于不同的子类【对象】有不同的行为表现】
实现多态的前提:
必须存在继承关系:多态必须发送在父类和子类之间
重写:子类重写父类方法
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。 “鸭子测试”可以这样表述:
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
Python 天生就是支持多态,因为他是弱类型语言,不需要指定类型。 Python“鸭子类型”。
class Animal:
'''
父类【基类】
'''
def say_who(self):
print('我是一个动物...')
pass
pass
class Duck(Animal):
'''
鸭子类【子类】【派生类】
'''
def say_who(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只漂亮的鸭子')
pass
pass
class Dog(Animal):
'''
小狗类【子类】【派生类】
'''
def say_who(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只哈巴狗')
pass
pass
class Cat(Animal):
'''
小猫类【子类】【派生类】
'''
def say_who(self):
'''
在这里重写父类的方法
:return:
'''
print('我是一只小猫')
pass
pass
def commonInvoke(obj):
'''
统一调用方法
:param obj: 对象的实例
:return:
'''
obj.say_who()
listobj=[Duck(),Dog(),Cat()]
for item in listobj:
commonInvoke(item)
'''
duck1=Duck()
duck1.say_who()
dog1=Dog()
dog1.say_who()
cat1=Cat()
cat1.say_who()
'''
多态:
可以增加程序的灵活性
增加程序的扩展性