一、封装
将变量和函数写入类中的操作即为封装,即类中封装了属性和方法。
通过封装,我们可以将一些细节隐藏起来(私有),只暴露出必要的接口供调用者使用。
1.1. 私有化
有时为了限制属性和方法只能在类内访问,外部无法访问;或父类中某些属性和方法不希望被子类继承。可以将其私有化。
1)单下划线:非公开API
大多数Python代码都遵循这样一个约定:有一个前缀下划线的变量或方法应被视为非公开的API,例如 _var1。这种约定不具有强制力。
2)双下划线:名称改写
有两个前缀下划线,并至多一个后缀下划线的标识符,例如 __x,会被改写为 _类名__x。只有在类内部可以通过 __x 访问,其他地方无法访问或只能通过 _类名__x 访问。
1.2. 私有属性
通过双下划线定义私有属性。
class Person:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
p = Person("张三")
print(p.get_name()) # 张三
print(p._Person__name) # 张三
print(p.__name) # 报错
1.3. 私有方法
通过双下划线定义私有属性。
class Person:
# 定义私有方法
def __private_method(self):
print("private method")
# 定义实例方法,调用私有方法
def do_something(self):
self.__private_method()
p = Person()
p.do_something() # private method
p._Person__private_method() # private method
p.__private_method() # 报错
1.4. property
1)方法转换为属性
可通过@property装饰器将一个方法转换为属性来调用。转换后可直接使用 .方法名 来使用,而无需使用 .方法名() 。
class Person:
def __init__(self, name):
self.name = name
@property
def eat(self):
print(f"{self.name} is eating...")
p = Person("张三")
p.eat # 张三 is eating...
2)只读属性
将方法名设置为去掉双下划线的私有属性名,方法中返回私有属性。
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
p = Person("张三")
print(p.name) # 张三
p.name = "李四" # 报错
3)读写属性
将方法名设置为去掉双下划线的私有属性名,使用 属性名.setter 装饰。
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
p = Person("张三")
print(p.name) # 张三
p.name = "李四"
print(p.name) # 李四
也可以在写方法中设置一些拦截条件来规范私有属性的写入。
class Person:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
if name == "李四":
print("不许叫李四")
else:
self.__name = name
p = Person("张三")
print(p.name) # 张三
p.name = "李四" # 提示 “不许叫李四”
print(p.name) # 张三
p.name = "王五"
print(p.name) # 王五
4)注意
@property装饰的方法不要和变量重名,否则可能导致无限递归。
class Person:
@property
def name(self):
return self.name
p = Person()
p.name # 报错:RecursionError: maximum recursion depth exceeded
二、继承
子类(派生类)继承父类(基类)中的属性和方法,实现代码重用。子类可以新增自己特有的方法,也可以重写父类的方法。
子类不能继承父类的私有属性和私有方法,因为存在名称改写。
1. 单继承
1)语法****
class 类名(父类):
类体
在类名后括号内指定要继承的父类。
2)案例
class Person:
"""人的类"""
home = "earth" # 定义类属性
def __init__(self, name):
self.name = name # 定义实例属性
def eat(self):
print("eating...")
class YellowRace(Person):
"""黄种人"""
color = "yellow" # 定义类属性
class WhiteRace(Person):
"""白种人"""
color = "white" # 定义类属性
class BlackRace(Person):
"""黑种人"""
color = "black" # 定义类属性
y1 = YellowRace("张三")
print(y1.home) # earth
print(y1.color) # yellow
print(y1.name) # 张三
y1.eat() # eating...
w1 = WhiteRace("李四")
print(w1.home) # earth
print(w1.color) # white
print(w1.name) # 李四
w1.eat() # eating...
b1 = BlackRace("王五")
print(b1.home) # earth
print(b1.color) # black
print(b1.name) # 王五
b1.eat() # eating...
2. 多继承
调用方法时先在子类中查找,若不存在则从左到右依次查找父类中是否包含方法。
1)语法
class 类名(父类1, 父类2, ...):
类体
2)案例
class Person:
"""人的类"""
home = "earth"
def __init__(self, name):
self.name = name
def eat(self):
print("eating...")
class YellowRace(Person):
"""黄种人"""
color = "yellow"
def run(self):
print("runing...")
class Student(Person):
"""学生"""
def __init__(self, name, grade):
self.name = name
self.grade = grade
def study(self):
print("studying...")
class ChineseStudent(Student, YellowRace): # 继承了Student和YellowRace
"""中国学生"""
country = "中国"
y1 = ChineseStudent("张三", "三年级")
print(y1.home, y1.color, y1.country, y1.name, y1.grade)
y1.eat()
y1.run()
y1.study()
3. 复用父类方法
子类可以在类中使用 super().方法名() 或 父类名.方法名() 来调用父类的方法。
1)super().方法名()
class Person:
"""人的类"""
home = "earth"
def __init__(self, name):
self.name = name
def eat(self):
print("eating...")
class YellowRace(Person):
"""黄种人"""
color = "yellow"
def run(self):
print("runing...")
class Student(Person):
"""学生"""
def __init__(self, name, grade):
self.name = name
self.grade = grade
def study(self):
print("先吃再学")
super().eat() # 子类中调用父类的方法
print("studying...")
class ChineseStudent(Student, YellowRace): # 继承了Student和YellowRace
"""中国学生"""
country = "中国"
y1 = ChineseStudent("张三", "三年级")
print(y1.home, y1.color, y1.country, y1.name, y1.grade)
y1.study()
2)父类名.方法名()
class Person:
"""人的类"""
home = "earth"
def __init__(self, name):
self.name = name
def eat(self):
print("eating...")
class YellowRace(Person):
"""黄种人"""
color = "yellow"
def run(self):
print("runing...")
class Student(Person):
"""学生"""
def __init__(self, name, grade):
self.name = name
self.grade = grade
def study(self):
print("先吃再学")
Person.eat(self) # 子类中调用父类的方法
print("studying...")
class ChineseStudent(Student, YellowRace): # 继承了Student和YellowRace
"""中国学生"""
country = "中国"
y1 = ChineseStudent("张三", "三年级")
print(y1.home, y1.color, y1.country, y1.name, y1.grade)
y1.study()
4. 方法解析顺序
方法解析顺序(mro—Method Resolution Order)。可使用 类名.mro 访问类的继承链来查看方法解析顺序。
class Person:
"""人的类"""
home = "earth"
def __init__(self, name):
self.name = name
def eat(self):
print("eating...")
class YellowRace(Person):
"""黄种人"""
color = "yellow"
def run(self):
print("runing...")
class Student(Person):
"""学生"""
def __init__(self, name, grade):
self.name = name
self.grade = grade
def study(self):
print("先吃再学")
Person.eat(self)
print("studying...")
class ChineseStudent(Student, YellowRace):
"""中国学生"""
country = "中国"
y1 = ChineseStudent("张三", "三年级")
print(
ChineseStudent.__mro__
)
# (<class '__main__.ChineseStudent'>, <class '__main__.Student'>, <class '__main__.YellowRace'>, <class '__main__.Person'>, <class 'object'>)
5. 方法重写
在子类中定义与父类方法重名的方法,调用时会调用子类中重写的方法。
class Person:
home = "earth"
def __init__(self, name):
self.name = name
def eat(self):
print("eating...")
class Chinese(Person):
color = "yellow"
# 重写父类方法
def eat(self):
print("用筷子吃")
y1 = Chinese("张三")
y1.eat()
注意:子类重写 init() 并调用时,不会执行父类的 init() 方法。如有必要,需在子类 init() 中使用 super().init() 来调用父类的 init() 方法 。
class Person:
def __init__(self, name):
self.name = name
class Chinese(Person):
def __init__(self, name, area):
super().__init__(name) # 调用父类的__init__()
self.area = area
y1 = Chinese("张三", "北京")
print(y1.name, y1.area)
三、多态
同一事物在不同场景下呈现不同状态。
class Animal:
def go(self):
pass
class Dog(Animal):
def go(self):
print("跑")
class Fish(Animal):
def go(self):
print("游")
class Bird(Animal):
def go(self):
print("飞")
def go(animal):
animal.go() # 将不同的实例传入,执行不同的方法
dog = Dog()
fish = Fish()
bird = Bird()
go(dog)
go(fish)
go(bird)