一 三大特性

  定义 基本使用 示例
封装 

基于类与对象的语法结构,把代表数据的变量和操作数据的函数进行封装成一个类或对象,通过类与对象语法对外公开少部分的数据操作

实现对类或对象的成员保护与访问机制避免外界随意修改删除破坏类的内部数据的合理性

 

访问权限:

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
示例1

 

属性封装

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 得到的结果还是原来的
对象属性的存取器@property 只读器/@属性名.setter 只写器
继承 使用已有父类作为基础创建子类的一种方式。子类的定义可以增加新的属性或方法,也可以复用或覆盖父类已有的属性和方法 

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 继承以后子类重写父类方法,不同的子类对于父类的同一个方法可以有不同的实现方式。龙生九子,子子不同。
2 支持鸭子类型(DuckType)

 

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):将不同的职责功能分开封装到不同的函数、类或模块中。尽量一个类/模块/函数只完成一件事情。

  • 开闭原则(Open Closed Principle,简称OCP):对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。对扩展开放即基于多态思想,允许子类重写父类方法,让不同子类在调用同一个父类方法时效果不同。对修改进行封闭的,即基于封装思想,选择性开放修改数据的操作接口。

  • 里氏替换原则(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
示例

参考:__str__和__repr__的区别

__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循环时,自动执行
__setitem__ 序列协议方法,当对象被当成序列操作,设置索引/键时,自动执行
'''序列协议方法,当对象被当成序列操作,设置、读取、删除索引/键时,自动执行。'''
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__ 序列协议方法,当对象被当成序列调用,删除索引/键时,自动执行
posted on 2022-04-24 23:23  大明花花  阅读(62)  评论(0编辑  收藏  举报