封装
封装(Encapsulation)是一种面向对象编程的基本概念,指的是将对象的状态(数据)和行为(方法)绑定在一起,并将其保护起来,防止外部直接访问或修改。封装的目的是确保对象的数据只通过合法的接口进行访问和修改,以提高代码的安全性、可维护性和灵活性
私有化
为了限制某些属性和方法仅在类内部访问(对外部不可见),或者避免父类中的某些属性和方法被子类继承,可以将其私有化
单下划线:非公开API
大多数Python代码都遵循这样一个约定:有一个前缀下划线的变量或方法应被视为非公开的API,例如_var1。这种约定不具有强制力。
class Person: def __init__(self,name,age): self.name = name self._age = age person1 = Person("Lucy",18) print(person1.name) print(person1._age) # 18双下划线:名词改写
有两个前缀下划线,并至多一个后缀下划线的标识符,例如__x,会被改写为_类名__x。只有在类内部可以通过__x访问,其他地方无法访问或只能通过_类名__x访问。名词改写是python属性私有化的底层逻辑
class Person: def __init__(self,name,age): self.name = name # 私有属性 self.__age = age # 私有方法 def __run(self): print(f"我叫{self.name},我今年{self.__age}") person1 = Person("Lucy",18) print(person1.name) print(person1._Person__age) # 18 print("*"*20) person1._Person__run() # 我叫Lucy,我今年18Property
# 其他方法对私有属性的操作 class Person: def __init__(self,name,age): self.name = name self.__age = age def setAge(self, age): if age < 18: self.__age = age else: self.__age = 18 def getAge(self): if self.__age>18: return 18 else: return self.__age person1 = Person("Lucy",16) print(person1.getAge()) # 16 person1.setAge(20) print(person1.getAge()) # 18在python中property的主要作用是将方法“伪装”成普通属性,从而在获取、设置、删除属性时添加额外的逻辑(如校验、计算、日志)
方法转换为属性
可通过@property装饰器将一个方法转换为属性来调用。转换后可直接使用 .方法名来使用,而无需使用 .方法名()
class Person: def __init__(self,name,age): self.name = name self.__age = age @property def age(self): # 这一步是读取操作,property修饰的方法名用私有属性的名字,对外暴露age属性,内部用 __age 存储原始数据 if self.__age>18: return 18 else: return self.__age person1 = Person("Lucy",16) print(person1.age) # 16读写属性
将方法名设置为去掉双下划线的私有属性名,使用 属性名.setter 装饰
class Person: def __init__(self,name,age): self.name = name self.__age = age @property def age(self): # property修饰的方法名用私有属性的名字,对外暴露age属性,内部用 __age 存储原始数据 # 读取私有属性值 if self.__age>18: return 18 else: return self.__age @age.setter def age(self,age): # 为私有属性赋值 if age<18: self.__age = age else: self.__age = 18 person1 = Person("Lucy",12) print(person1.age) # 12 person1.age = 14 print(person1.age) # 14继承
子类(派生类)继承父类(基类)中的属性和方法,实现代码重用。子类可以新增自己特有的方法,也可以重写父类的方法。
子类不能继承父类的私有属性和私有方法,因为存在名称改写,但是可以通过改写后的名称直接访问父类的私有成员,不过,这种做法违背了封装原则,不建议使用。
# 破坏封装的不良做法 class BankAccount: def __init__(self, balance): self.__balance = balance def deposit(self, amount): if amount > 0: self.__balance += amount else: raise ValueError("存款金额必须为正") def get_balance(self): return self.__balance class HackerAccount(BankAccount): def steal_money(self): # 直接访问父类的私有属性 self._BankAccount__balance = 1000000 account = HackerAccount(100) account.steal_money() print(account.get_balance()) # 1000000单继承
""" 语法:class 类名(父类): 类体 """ class Person: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def hobby(self): print("ctrl") class man(Person): # 单继承 当之类不增加额外的属性,init不需要写 def self_introduction(self): print(f"名字:{self.name},年龄:{self.age},性别:{self.gender}") def hobby(self): # 重写方法 print("play game") man1 = man("Jack",18,"male") man1.self_introduction() man1.hobby()多继承
调用方式时现在子类中查找,若不存在则从左到右依次查找父类中是否包含方法
""" 语法:class 类名(父类1, 父类2, ...): 类体 """ class Person: def __init__(self, name, age): self.name = name self.age = age def hobby(self): print("Person's hobby: reading") def introduce(self): print(f"I'm {self.name}, {self.age} years old.") class Worker: def __init__(self, company): self.company = company def work(self): print(f"Working at {self.company}") def hobby(self): print("Worker's hobby: sleeping") # 多继承:同时继承 Person 和 Worker class StudentWorker(Person, Worker): def __init__(self, name, age, company, school): # 分别调用两个父类的初始化方法 Person.__init__(self, name, age) Worker.__init__(self, company) self.school = school def study(self): print(f"Studying at {self.school}") sw = StudentWorker("Alice", 22, "Microsoft", "Stanford") sw.introduce() sw.work() sw.study()复用父类方法
子类可以在类中使用 super().方法名() 或 父类名.方法名() 来调用父类的方法
class Person: def __init__(self, name, age): self.name = name self.age = age def hobby(self): print("Person's hobby: reading") def introduce(self): print(f"I'm {self.name}, {self.age} years old.") class Worker: def __init__(self, company): self.company = company def work(self): print(f"Working at {self.company}") def hobby(self): print("Worker's hobby: sleeping") # 多继承:同时继承 Person 和 Worker class StudentWorker(Person, Worker): def __init__(self, name, age, company, school): # 分别调用两个父类的初始化方法 Person.__init__(self, name, age) Worker.__init__(self, company) self.school = school def study(self): # super().方法名() super().introduce() print(f"Studying at {self.school}") # 类名.方法名() Person.introduce(self) sw = StudentWorker("Alice", 22, "Microsoft", "Stanford") sw.introduce() sw.work() sw.study()方法解析顺序
方法解析顺序(mro—Method Resolution Order)。可使用 类名.mro访问类的继承链来查看方法解析顺序
class Person: def __init__(self, name, age, **kwargs): super().__init__(**kwargs) # 调用 MRO 链上的下一个 __init__ self.name = name self.age = age def hobby(self): print("Person's hobby: reading") def introduce(self): print(f"I'm {self.name}, {self.age} years old.") class Worker: def __init__(self, company, **kwargs): super().__init__(**kwargs) # 调用 MRO 链上的下一个 __init__ self.company = company def work(self): print(f"Working at {self.company}") def hobby(self): print("Worker's hobby: sleeping") class StudentWorker(Person, Worker): def __init__(self, name, age, company, school, **kwargs): super().__init__(name=name, age=age, company=company, **kwargs) self.school = school def study(self): super().introduce() # 调用 MRO 链上第一个 introduce() print(f"Studying at {self.school}") print(StudentWorker.__mro__) # (<class '__main__.StudentWorker'>, <class '__main__.Person'>, <class '__main__.Worker'>, <class 'object'>) sw = StudentWorker("Alice", 22, "Microsoft", "Stanford") sw.introduce() # 来自 Person sw.work() # 来自 Worker sw.study() # study() 使用 super().introduce() 调用 MRO 上第一个 introduce()方法重写
在子类中定义与父类方法重名的方法,调用时会调用子类中重写的方法
class Animal: def speak(self): print("Animal makes a sound") class Dog(Animal): def speak(self): # 重写父类的 speak 方法 print("Dog barks") class Cat(Animal): def speak(self): # 重写父类的 speak 方法 print("Cat meows") a = Animal() d = Dog() c = Cat() a.speak() # Animal makes a sound d.speak() # Dog barks c.speak() # Cat meows⚠️:子类重写init()并调用时,不会执行父类的init()方法。如有必要,需在子类init()中使用super().init()来调用父类的init()方法
多态
同一事物在不同场景下呈现不同状态
class Animal: def speak(self): print("动物发出声音") class Dog(Animal): def speak(self): print("小狗:汪汪汪") class Cat(Animal): def speak(self): print("小猫:喵喵喵") def make_sound(animal): animal.speak() dog = Dog() cat = Cat() make_sound(dog) make_sound(cat)