一 三大特性
定义 | 基本使用 | 示例 | |
封装 |
|
访问权限: Python对成员的保护等级只有两个:私有的(private)(——属性/方法)和公有的(public) |
![]() class Person(object): def __init__(self, name, money, age, password, num): self.name = name # 公有属性 self.__num = num self.__money = money # 私有属性 self.__password = password # 私有属性 def __test1(self):#私有方法 return 123 def get_money(self, password): if self.__check_password(password): # 通过在return在类的内部提供操作数据的接口方法给外界去获取、修改数据 return self.__money else: raise Exception("密码不正确!没钱!") def save_money(self, money, password): """存钱或者取钱""" if not self.__check_password(password): return "密码不正确!无法操作!" if money > 0: # 存钱 self.__money += money return # 取钱 if abs(money) > self.__money: return "余额不足!" self.__money += money def __check_password(self, password): """检测密码,私有方法""" return password == self.__password xiaoming = Person("小明", 1000, 17, "123456", 1) # 类的外界,可以访问公有属性,无法访问私有属性 print(xiaoming.name) #小明 """封装其实就是为了让一些属性和方法,不能被外界直接修改和调用而已""" # print(xiaoming.__test1) # 报错!无法访问私有方法 # print(xiaoming.__num) # 报错! 无法访问私有属性 'Person' object has no attribute '__num' """对数据的修改,可以通过提供公有方法的方式给外界操作,那么外界操作时,我们就可以设置一些判断""" print(xiaoming.get_money("123456")) #1000 xiaoming.save_money(-150, "123456") print(xiaoming.get_money("123456")) #850
|
属性封装 |
![]() import hashlib class Person(object): def __init__(self, name, money, password): self.name = name self.__money = money self.__password = self.__pwd(password) @property #装饰器装饰接口 def money(self): # money方法因为上面加了@perperty,所以变成了只读属性方法,实际使用时,直接当成属性来进行赋值操作即可 return self.__money @money.setter #装饰器的调用 def money(self, money): if money < 0: print("无法修改!") return self.__money = money # 对象属性的存取器,@property 只读器, @属性名.setter 只写器 @property def password(self): return self.__password @password.setter def password(self, new_password): self.__password = self.__pwd(new_password) def __pwd(self, raw_password): hash = hashlib.sha256() hash.update(raw_password.encode()) return hash.hexdigest() xiaoming = Person("小明", 1010, "123456") print(xiaoming.money) #1010 print(xiaoming.password) #8d969eef6ecad3c2.. xiaoming.password = "123123123" print(xiaoming.password) #932f3c1b56257c.. xiaoming.money = 100 print(xiaoming.money) #100 xiaohong = Person("小红", 2000, "123456") print(xiaohong.money) #2000 xiaohong.money = -1000 #无法修改! print(xiaohong.money) #2000 得到的结果还是原来的 |
||
继承 | 使用已有父类作为基础创建子类的一种方式。子类的定义可以增加新的属性或方法,也可以复用或覆盖父类已有的属性和方法 |
1、子类只能继承父类的公有属性和方法 2、子类可以拥有父类所没有的属性和方法,即子类可以对父类进行扩展。 3、子类可以用自己的方式重写父类的方法。 |
![]() 基本使用:(单继承) class Person(object): """人类""" def __init__(self, name, age): self.name=name self.age=age def eat(self): print("eating...") def sleep(self): print("sleep...") class Student(Person): """学生类""" def __init__(self, name, age, achievement): #super就是一个内置函数,用于调用父类的属性或方法,一般写法:super(子类名, self).方法(参数列表) super().__init__(name, age) self.achievement = achievement """继承不仅可以复用父类的方法,也可以复用父类的属性,甚至增加属于自己的属性。""" def do_homework(self): print("do_homework.....") def do_read(self): print("reading........") '''继承过程中,子类不仅可以复用父类的方法,还可以重写父类的方法。''' def sleep(self, where): if where != "课堂": super().sleep() else: print("不能睡觉!") class Teacher(Person): """老师类""" def __init__(self, name, age, salary): super().__init__(name, age) self.salary = salary def teaching(self): print("teaching.....") s = Student("小明", 17, 100) s.sleep("课堂") 多继承: """多继承,就是一个类同时继承多个父类的特性""" class Animal(object): def eat(self): print("eating...") def sleep(self): print("sleep...") class Fly(object): def fly(self): print("fly...") class Eagle(Animal, Fly): def hunt(self): print("hunt....") class Plane(Fly): def payload(self): print("运输....") 菱形继承: class Human(object): id = 1 def eat(self): print("1-饿了去打猎") print(self.id) print("2-抓到猎物,直接生吃!") class Man(Human): id = 2 def eat(self): print("3-饿了去买酒卖肉") print(super().id) print("4-一边喝酒,一边吃肉!") class Woman(Human): id = 3 def eat(self): print("5-饿了去煮饭买菜") print(super().id) print("6-动作优雅,小口尝试!") class Children(Man, Woman): id = 4 def eat(self): print("7-饿了就哭.") print(super().id) #2 print("8-吃了接着哭。") xm = Children() xm.eat() print(Children.__mro__) #(<class '__main__.Children'>, <class '__main__.Man'>, <class '__main__.Woman'>, <class '__main__.Human'>, <class 'object'>)
|
多态 |
弱类型语言: 一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。 |
1 继承以后子类重写父类方法,不同的子类对于父类的同一个方法可以有不同的实现方式。龙生九子,子子不同。
|
![]() class Payment(object): def __init__(self, name, money): self.money = money self.name = name def pay(self, *args, **kwargs): pass class AliPay(Payment): def pay(self): # 支付宝支付渠道 print('%s通过支付宝消费了%s元' % (self.name, self.money)) class WeChatPay(Payment): def pay(self): # 微信支付渠道 print('%s通过微信消费了%s元' % (self.name, self.money)) class Order(object): # 只需要让传入的对象可以调用pay方法,而不关心具体是什么函数。 def account(self, pay_obj): pay_obj.pay() class Test(object): def pay(self): print("测试功能") '''继承以后子类重写父类方法''' pay1 = WeChatPay("小红", 100) pay2 = AliPay("小明", 200) #class必须实例化后才能使用 order = Order() order.account(pay1) # True 小红通过微信消费了100元 order.account(pay2) # True 小明通过支付宝消费了200元 '''支持鸭子类型''' t = Test() #有pay函数 order.account(t) #False 测试功能 # 在所有的编程语言中,只要是弱类型的面向对象编程语言,多态都是不明显的,而是大多都支持鸭子类型。 |
二 面向对象五个基本原则(简称:SOLID)
面向对象五个基本原则的本质就是追求提高代码质量,尽量达到高内聚,低耦合的效果。
-
单一职责(Single Responsibility Principle,简称SRP):将不同的职责功能分开封装到不同的函数、类或模块中。尽量一个类/模块/函数只完成一件事情。
-
-
里氏替换原则(Liskov Substitution Principle,简称LSP):派生类(子类)可以在代码中代替其基类(超类)对象。
-
接口隔离原则(Interface Segregation Principle,简称ISP):一个类中如果有调用了另一个类的方法或属性时,应该将依赖性建立在最小的接口上的,避免出现一个类修改了影响污染另一个类。
-
依赖反转原则(Dependence Inversion Principle,简称DIP):程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
除此之外,还有面向对象七大原则的说法,在上面五大原则的基础上面,后人又增加了两大原则:
- 迪米特原则(Law of Demeter,最小认知原则):一个类对于其他类知道的越少越好,可以有效的降低类与类之间的耦合性。
- 组合/聚合复用原则(Composite/Aggregate Reuse Principle):尽量使用使用多个类组合/聚合,不要使用滥用类继承。
三 魔术方法
在特定条件下会自动执行的方法(函数),不需要我们自己手动调用。类中所有魔术方法都是私有方法,只会在类的内部自动执行,无法被外界调用
固定方法名 | 描述 | 示例 | |
---|---|---|---|
__init__ |
构造方法,当对象初始化会自动调用,常用于对象属性的初始化。 |
![]() class Persion(object): def __new__(cls, *args, **kwargs): #__new__ 用来制造对象 print("Person.__new__ 被调用了") # return object.__new__(cls) return object.__new__(cls) # object提供的__new__是魔术静态方法,所以需要传递当前类作为参数,才能创建当前类的实例对象 def __init__(self, name, age): #__init__ 用来初始化对象 print("Person.__init__被调用了") self.name = name self.age = age '''在自动执行 `__init__()`方法前,会先执行 object.`__new__`方法''' '''`__new__`方法,在内存中开辟对象空间并返回对象提供给`__init__()''' xiaoming = Persion("小明", 16) #Person.__new__ 被调用了 Person.__init__被调用了 # print(xiaoming.name) # print(xiaoming.age) ![]() 1 '''`__init__` 只能初始化当前类刚创建的对象,自动调用。 `__new__`可以返回其他类的对象,但不会触发当前类的`__init__`魔术方法。所以务必要返回当前类对象''' class Animal(object): def __init__(self): print("Animal.__init__") class Person(object): # __init__ 不是创建对象的魔术方法,而是刚创建(刚出生)的对象进行属性默认值的设置 def __init__(self, name, age): print("Person.__init__") # __init__执行之前,类就已经完成了对象的创建 print(self) # <__main__.Person object at 0x7fa385167fd0> self.name = name self.age = age def __new__(cls, *args, **kwargs): print("Person.__new__") # __new__ 必须有返回值,而且返回值应该是当前类的实例对象. # return object.__new__(cls) # 否则不会触发当前类的__init__方法,而是触发了其他类的__init__方法 return Animal() p1 = Person("小明", 13) #Person.__new__ Animal.__init__ print(p1) #<__main__.Animal object at 0x10257d4f0> print(type(p1)) #<class '__main__.Animal'> 2 '''单例模式''' '''基于`__new__`上面的特点,我们可以控制类创建对象的过程。实现在整个程序运行过程中,保证一个类不管实例化多少次,生成的都是同一个对象。例如,购物车,日志,数据库操作等''' class Singleton(object): # 定义一个私有的成员属性__instance , 用来存储接下来创建的唯一实例对象. __instance = None def __new__(cls, *args, **kwargs): # 如果类的私有成员属性__instance 不存在 是一个None, 那么通过父类object的new创建一个对象 if cls.__instance is None: # 创建对象,把对象存在私有属性__instance当中 # cls.__instance = super().__new__(cls) cls.__instance = object.__new__(cls) #创建并返回一个新对象 # 将该类对象直接返回 return cls.__instance class Log(Singleton): def __init__(self, name): self.name = name # 第一次 触发cls.__instance is None 结果为True, 所以创建对象 logger1 = Log("small_project") print(logger1) # <__main__.Log object at 0x7fbb670f9fa0> # 第二次 触发__new__ 发现cls.__instance is None 结果为False 直接 return cls.__instance logger2 = Log("small_project") print(logger2) # <__main__.Log object at 0x7fbb670f9fa0> print(logger1 is logger2) # True !单例模式 |
|
__new__ |
创建实例对象方法,是调用类时自动执行的第1个魔术方法,也是一个类方法。 比__init__ 的执行时间要早,返回值是实例对象,也就是__init__ 中的self。 |
||
__call__ |
可调用协议方法,当对象被当成函数时,自动执行 |
![]() class Person(object): def __init__(self, name, age): self.name = name self.age = age # 类的内部实现了__call__方法,则表示当前类遵循了可调用协议,这个类的所有实例对象都可以被当成函数进行调用 def __call__(self, *args, **kwargs): print("Person.__call__") print(f"args={args}", f"kwargs={kwargs}") return "<Person>" xiaoming = Person("小明", 16) # 把对象当成一个函数来调用,那么对象内部的__call__就会被调用 # 如果类内部没有声明__call__方法,则调用失败,程序报错! xiaoming("A", "B", "C", name="xiaoming", age=17) #Person.__call__ args=('A', 'B', 'C') kwargs={'name': 'xiaoming', 'age': 17} # 可以通过callable内置函数来判断对象的类的内部是否实现了__call__方法 # 当然,callable也可以用于判断一个变量是否是函数或对象/类方法 print( callable(xiaoming) ) # True # 因为Python中万物皆为对象,函数本身也是一个对象,创建函数的类是function,也在内部实现了__call__方法 print(type(函数名)) # <class 'function'> # 可以通过对象内置的属相__class__可以查看当前对象是属于哪个类型/或哪个类创建出来的 print(xiaoming.__class__) # <class '__main__.Person'> |
|
__str__ |
可打印协议方法,当对象被print打印时,自动执行 |
![]() # 如果希望打印对象的时候,可以输出一些可控的信息,可以使用__str__或__repr__ class Person1(object): def __init__(self, name, sex): self.name = name self.sex = sex def __str__(self): # __str__必须有返回值,而且返回值的类型必须是str类型,否则报错 # __str__ 与 __repr__不能同时使用,否则只会输出__str__ return "hello" class Person2(object): def __init__(self, name, sex): self.name = name self.sex = sex # __repr__ 必须有返回值,而且返回值的类型必须是str类型,否则报错 def __repr__(self): return "xiuDog" p1 = Person1("小明", 16) p2 = Person2("小白", 16) print(p1) # hello print(p2) # xiuDog |
|
__repr__ |
可打印协议方法,当对象被终端模式下不使用print而直接输出时,自动执行,类似__str__ |
||
__enter__ |
执行上下文协议方法,当对象写在with语句后面时,自动执行 |
![]() '''基于with上下文管理器自定义文件操作对象等操作''' import pickle class Pickle(object): def __init__(self, path, mode="w"): self.path = path self.mode = mode self.file = None def __enter__(self):#打开 self.file = open(self.path, self.mode) return self def write(self, message): return pickle.dump(message, self.file) def read(self): return pickle.load(self.file) def __exit__(self, exc_type, exc_val, exc_tb):#关闭 self.file.close() with Pickle("1.db", "wb") as p: p.write("hello") with Pickle("1.db", "rb")as p: print(p.read()) #hello |
|
__exit__ |
执行上下文协议方法,当对象写在with语句后面,而with语句执行结束时,自动执行 | ||
__iter__ |
可迭代协议方法,当对象被当成可迭代对象调用,使用iter函数或者for循环时,自动执行 |
![]() set_data = {"A", "B", "D", 100} # data = iter(set_data) # 方法一. 可迭代对象转化迭代器 data = set_data.__iter__() # 方法二 print(data) #<set_iterator object at 0x107826040> res = data.__next__() print(res) #B res = data.__next__() print(res) #A res = next(data) # next(data),就是 data.__next(),本质上没有区别,仅仅写法不同。 print(res) #100 res = next(data) print(res) #D res = next(data) print(res) #StopIteration # 迭代器中所有值都被提取完成后,再次取值,python会以抛出一个StopIteration异常告诉我们,没有值了。这并不代表错误发生,而是一种迭代完成的标志,防止出现无限循环。 #迭代器的特性就是能够调用__next__方法依次计算出迭代器中的每一个值 ,然后每一个值重复的放在同一个内存空间中 |
|
__next__ |
迭代器协议方法,当对象被当成可迭代对象调用,for循环时,自动执行 | ||
|
序列协议方法,当对象被当成序列操作,设置索引/键时,自动执行 |
![]() '''序列协议方法,当对象被当成序列操作,设置、读取、删除索引/键时,自动执行。''' class Person(object): def __init__(self, name, age): self.name = name self.age = age def __getitem__(self, item): print('obj[key]取值时,执行__getitem__') print("取值为:",self.__dict__[item]) def __setitem__(self, key, value): print('obj[key]=value赋值时,执行__setitem__') self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]触发') self.__dict__.pop(key) def __len__(self): return len(self.__dict__) # obj.["key"]的方式触发__xxxitem__魔法方法 xiaoming = Person("小明", 16) name = xiaoming["name"] # 触发__getitem__执行 xiaoming["age"] = 18 # 触发__setattr__执行 del xiaoming["age"] # 触发__delitem__ print( len(xiaoming) ) #1 """ 字典/列表/元组/字符串等序列类型,之所以支持中括号取键/下标进行数据操作,就是因为这些数据都是对象, 而且实例化这些对象的类都统一的实现了序列协议方法 """ data = {"name": "xiaohong"} print(type(data), data.__class__) # <class 'dict'> <class 'dict'> data = [1, 2, 3, 4] print(type(data), data.__class__) # <class 'list'> <class 'list'> |
|
__getitem__ |
序列协议方法,当对象被当成序列调用,读取索引/键时,自动执行 | ||
__delitem__ |
序列协议方法,当对象被当成序列调用,删除索引/键时,自动执行 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现