一、类和实例
1. 定义
类:具有一些相同特征的物体
实例:确定的某一个个体
比如:人-----刘德华 汽车-----车牌号为xxxx的车 猪-----佩奇
2. 一般格式
class 类名:
def __init__(self, 形参, ...): #初始化方法
self.变量名=形参
...
方法...
1)根据约定,类名首字母大写,实例首字母小写
2)类中的函数称为方法,函数的一切都适用于方法,只是调用的方式不同
3)根据类创建实例时,会自动运行初始化方法
4)self必不可少,是一个指向实例本身的引用,让实例能够访问类中的属性和方法,每个实例方法的调用都会自动传递self,即不需要给self传值,需要给后面的形参传值
5)self.变量名可以供类中所有方法使用,可以通过实例名.变量名来访问变量,这种变量称为属性,查看实例的属性:实例名.__dict__
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
rec=Rectangle(2,1) #实例化一个长方形
print(rec.permeter()) #6
print(rec.area()) #2
print(rec.length) #2
print(rec.__dict__) #{'length': 2, 'width': 1}
3. 属性
1)默认值
跟之前函数里的形参默认值类似
class Rectangle():
def __init__(self,length=1,width=1): #给形参添加默认值
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
rec=Rectangle() #没有传任何实参
print(rec.permeter()) #4
print(rec.area()) #1
print(rec.__dict__) #{'length': 1, 'width': 1}
在初始化方法里指定,就不需要再写该形参了
class Rectangle():
def __init__(self,length,width):
self.length=length
self.width=width
self.color='black'
rec=Rectangle(2,1)
print(rec.color) #black
2)实例属性
定义在初始化方法内,要加self前缀,只能由实例调用,不能由类调用
class Rectangle():
def __init__(self,length,width):
self.length=length
self.width=width
rec=Rectangle(2,1)
print(rec.length) #2
print(Rectangle.length) #AttributeError: type object 'Rectangle' has no attribute 'length'
3)类属性
定义在类里,没有self前缀,实例和类都能调用
class Rectangle():
disc='两边的长相等,两边的宽相等,长和宽的夹角为90°'
rec=Rectangle()
print(rec.disc) #两边的长相等,两边的宽相等,长和宽的夹角为90°
print(Rectangle.disc) #两边的长相等,两边的宽相等,长和宽的夹角为90°
4)修改属性值
直接修改
class Rectangle():
def __init__(self,length,width):
self.length=length
self.width=width
rec=Rectangle(2,1)
print(rec.length) #2
rec.length=3 #访问属性修改length的值
print(rec.length) #3
通过类中的方法修改,这样就不需要在外面访问属性
class Rectangle():
def __init__(self,length,width):
self.length=length
self.width=width
def update_length(self,new_length): #定义一个修改长的方法
self.length=new_length
def update_width(self,new_width): #定义一个修改宽的方法
self.length=new_width
rec=Rectangle(2,1)
print(rec.length) #2
rec.update_length(3) #调用修改长的方法
print(rec.length) #3
对于类属性,可以通过类名.属性名永久修改属性值,通过实例名.属性名修改只对当前实例有效
class Rectangle():
disc='两边的长相等,两边的宽相等,长和宽的夹角为90°'
rec=Rectangle()
rec.disc='这是一个长方形' #临时修改类属性
print(rec.disc) #这是一个长方形
print(Rectangle.disc) #两边的长相等,两边的宽相等,长和宽的夹角为90°
rec1=Rectangle() #新建一个实例
print(rec1.disc) #两边的长相等,两边的宽相等,长和宽的夹角为90°
Rectangle.disc='这是一个长方形' #永久修改类属性
print(rec.disc) #这是一个长方形
print(Rectangle.disc) #这是一个长方形
rec2=Rectangle() #新建一个实例
print(rec2.disc) #这是一个长方形
5)将实例用作属性
在比较复杂的类中,可以将类划分成一个个小类,这样就会使得代码清晰易懂
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
self.disc=Discription()
class Discription:
def __init__(self,disc='这是一个长方形'):
self.disc=disc
def discribe_rectangle(self):
print('两边的长相等,两边的宽相等,长和宽的夹角为90°')
rec=Rectangle(2,1)
print(rec.disc.disc) #这是一个长方形 rec.disc创建了一个Discription的实例,通过rec.disc.disc访问disc属性
rec.disc.discribe_rectangle() #两边的长相等,两边的宽相等,长和宽的夹角为90°
4. 方法
1)实例方法
只能由实例调用,不能由类调用
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
rec=Rectangle(2,1) #实例化一个长方形
print(rec.permeter()) #6
print(Rectangle.permeter()) #TypeError: permeter() missing 1 required positional argument: 'self'
2)类方法
实例和类都能调用
在类方法的上一行加一个装饰器@classmethod,表示下面的方法都是类方法
cls必不可少,约定实例方法用self,类方法用cls
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
@classmethod
def discription(cls):
print('两边的长相等,两边的宽相等,长和宽的夹角为90°')
rec=Rectangle(2,1) #实例化一个长方形
rec.discription() #两边的长相等,两边的宽相等,长和宽的夹角为90°
Rectangle.discription() #两边的长相等,两边的宽相等,长和宽的夹角为90°
print(type(rec.discription)) #<class 'method'>
3)静态方法
实例和类都能调用
在类方法的上一行加一个装饰器@staticmethod,表示下面的方法都是静态方法
本质上是函数,只是写在了类里面,不依赖类和实例
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
@staticmethod
def sum(a,b):
return a+b
rec=Rectangle(2,1) #实例化一个长方形
print(rec.sum(1,2)) #3
print(Rectangle.sum(1,2)) #3
print(type(rec.sum)) #<class 'function'>
5. 导入其他模块的类
方法跟导入其他模块的函数类似,把函数名换成类名就可以了,具体请参考另一篇笔记:Python学习笔记-函数,模块,包-CSDN博客
二、继承
1. 定义
编写类可以不用从0开始,如果要编写的类跟一个现成的类很类似,可以使用继承
父类:原有的类
子类:新类
继承时,子类会自动获得父类的所有属性和方法,同时可以定义自己的属性和方法
2. 格式
class 子类名(父类名):
...
对于任何一个父类而言,名称后面的括号可加可不加,因为所有的类都是object的子类,这也是为什么类会有自带的很多方法和属性,以下三种写法结果是一致的
class Rectangle:
pass
class Rectangle():
pass
class Rectangle(object):
pass
object类的一些方法
class Rectangle:
pass
print(Rectangle.__name__) #Rectangle 显示类的名称
print(Rectangle.__bases__) #(<class 'object'>,) 显示父类的名称
3.继承方式
1. 完全继承
子类跟父类完全一样,这种情况下,跟父类的实例没有区别,所以完全继承没有必要
class Rectangle: #父类
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
class Square(Rectangle): #子类
pass #占位符,防止语法错误
sq=Square(2,2)
print(sq.permeter()) #8
print(sq.area()) #4
2. 全部重写
相当于没有继承,所以这种也没必要
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
class Square(Rectangle):
def __init__(self,side):
self.side=side
def permeter(self):
return (self.side)*4
def area(self):
return self.side**2
sq=Square(2)
print(sq.permeter()) #8
print(sq.area()) #4
3. 部分继承
有父类的部分方法或属性,也有自己的方法或属性
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
class Square(Rectangle):
def __init__(self,length,width):
self.length=length
self.width=width
self.side=length #子类自己的属性
def half_permeter(self): #子类自己的方法
return self.length+self.width
sq=Square(2,2)
print(sq.permeter()) #8
print(sq.half_permeter()) #4
print(sq.area()) #4
print(sq.side) #2
当子类的方法与父类的方法重名,会覆盖父类的方法,即重写父类的方法
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
def permeter(self):
return (self.length+self.width)*2
def area(self):
return self.length*self.width
class Square(Rectangle):
def permeter(self): #与父类方法同名,覆盖父类方法
return self.length+self.width
sq=Square(2,2)
print(sq.permeter()) #4 调用的是子类中的方法
print(sq.area()) #4
如果既要继承父类的方法或属性,同时又要在方法中增加一些新的代码,或者增加一些新的属性,可以使用super()
class Rectangle:
def __init__(self,length,width):
self.length=length
self.width=width
@classmethod
def discription(cls):
print('两边的长相等,两边的宽相等,长和宽的夹角为90°')
class Square(Rectangle):
def __init__(self,length,width): #与父类方法同名
super().__init__(length,width) #使用super()继承父类的属性
self.side=length #新增自己的属性
@classmethod
def discription(cls): #与父类方法同名
super().discription() #使用super()继承父类方法中的代码
print('长和宽也相等') #新增自己的代码
sq=Square(2,2)
print(sq.side) #2
sq.discription()
'''
两边的长相等,两边的宽相等,长和宽的夹角为90°
长和宽也相等
'''
4. 私有属性与私有方法
私有属性:不能被子类继承,也不能被自己实例使用的属性,格式是__属性名
私有方法:不能被子类继承,也不能被自己实例使用的方法,格式是__方法名
如果前后都有__: 类中自带的属性或方法
class Class1:
def __init__(self):
__private_attribute='私有属性'
def __private_method(self):
print("私有方法")
class Class2(Class1):
pass
cls1=Class1()
cls2=Class2()
cls1.__private_attribute #AttributeError: 'Class1' object has no attribute '__private_attribute'
cls1.__private_method() #AttributeError: 'Class1' object has no attribute '__private_method'
cls2.__private_attribute #AttributeError: 'Class2' object has no attribute '__private_attribute'
cls1.__private_method() #AttributeError: 'Class2' object has no attribute '__private_method'
私有属性和私有方法可以在类内部通过self访问
如果想要在外部获取私有属性和私有方法,可以通过在类里面定义非私有方法,在这个方法里调用私有属性或私有方法
class Class1:
__private_attribute='私有属性'
def __private_method(self):
print("私有方法")
def use_private(self):
print(self.__private_attribute)
self.__private_method()
class Class2(Class1):
pass
cls1=Class1()
cls2=Class2()
cls1.use_private()
cls2.use_private()
'''
私有属性
私有方法
私有属性
私有方法
'''
5. 多继承
一个子类继承多个父类,可以继承父类所有能够被继承的属性和方法
class Class1:
attr1='attr1'
def method1(self):
print("Class1_method1")
class Class2:
attr2='attr2'
def method2(self):
print("Class2_method2")
class Class3(Class1,Class2):
pass
cls3=Class3()
print(cls3.attr1) #attr1
print(cls3.attr2) #attr2
cls3.method1() #Class1_method1
cls3.method2() #Class2_method2
如果不同父类中同名方法,按顺序继承第一个父类的方法,注意:初始化方法只执行第一个父类中的
class Class1:
attr1='attr1'
def method(self):
print("Class1_method1")
class Class2:
attr2='attr2'
def method(self):
print("Class2_method2")
class Class3(Class1,Class2):
pass
cls3=Class3()
cls3.method() #Class1_method1
6. 多态
一个父类可以有多个子类,这些子类可以重写父类中同一个方法,当使用这个方法时,可以通过一个接口(函数),传入不同子类的实例(对象),得到不同的结果
class Animal: #父类
def sound(self):
pass
class Dog(Animal): #Animal的子类:Dog
def sound(self): #重写sound方法
print('汪汪汪')
class Cat(Animal): #Animal的子类:Cat
def sound(self): #重写sound方法
print('喵喵喵')
def animal_sound(obj): #定义一个调用不同子类sound方法的函数
obj.sound()
dog=Dog() #实例化Dog类
cat=Cat() #实例化Cat类
animal_sound(dog) #汪汪汪 调用animal_sound函数,传入dog对象,得到'汪汪汪'
animal_sound(cat) #喵喵喵 调用animal_sound函数,传入cat对象,得到'喵喵喵'