zoukankan      html  css  js  c++  java
  • Python 面向对象编程

    面向对象的三大特性:

    1.封装

    2.继承

    3.多态(python中不存在的,而是鸭子类型)

    在python中,一切皆对象,对象是某个东西。所以,顾名思义,类当然也是对象,连一个数字、字符串都是对象。

    面向对象编程,是一种哲学,编程的哲学、编程的思维。很虚的,只是指导你如何去思考。

    面向对象编程,就是将数据与函数绑定到一起,进行封装。

    一、面向对象

    先用现实生活中的一个例子引入:

    见过月饼没?(没见过的出门左拐)

    要做出一个月饼,首先得要做出月饼的模子,有了这个模子,就可以照着这个模子无限做月饼。

    类:就相当于模子。

    对象:就是做好的一个个的月饼。(python中,对象是可变的)

    二、类、实例 

    (一).类(模子)

    类有它自己的特征(属性)、行为(方法)。

    拿“犬类”来理解:

    狗的特征(属性):毛色、体重、血统……

    狗的行为(方法):吃东西、奔跑、汪汪叫……

    特征说白了就是属性,在编程中实则就是变量;行为就是方法,在编程中实则就是函数。

    但是,方法和函数是有区别的:与类有着特定关联的函数,才被叫方法。

    这里引入一个isinstance()这个内置函数:

    class Dog:
        pass
    
    class Cat:
        pass
    
    d1 = Dog()  # 实例化一个类。加括号,就像调用函数一样
    d2 = Dog()
    
    c1 = Cat()
    
    print(d1 is d2)  # False。看清楚了,这里是 is,身份运算符,判断id是不是一样的。d1、d2是两个不同的实例,当然ID不一样了
    print(isinstance(d1, Dog))  # True
    print(isinstance(c1, Cat))  # True
    print(isinstance(c1, Dog))  # False
    print(isinstance(c1, (Dog, Cat)))  # True,判断实例是不是属于某一个类,一个为真即为真
    View Code

    (二).实例

    模子创建出来的一个具体的东西。月饼就是由模子做出来的,月饼就是个具体的东西。

    每一个实例之间互不相干,可以实例化无限个(只要内存够用)。

    class Cat:
        pass
    
    c1 = Cat()  # 一定要加括号,就像调用函数一样

    (三).举例理解

    老虎、狮子、猎豹,都是猫科动物这一类的。是真实存在的,是猫科动物的具体实例的对象。

    而类,则是猫科动物的模版,因为不存在一个叫猫科动物的动物。

    三、属性封装

    class Cat:
        var1 = "一只猫"
    
    print(Cat.var1)  # 一只猫。直接通过 "类名.变量名" 来访问
    
    Cat.var2 = "两只猫"  # 只有python可以这么做
    print(Cat.var2)  # 两只猫
    
    c1 = Cat()  # 把Cat类实力化
    print(c1.var1)  # 一只猫
    print(c1.var2)  # 两只猫
    View Code

    查找变量,首先会去实例中找,没找到再去类中找。

    (一)."属性操作"内置函数

    getattr()/hasattr()/setattr()/delattr() 其实针对的是变量空间

    这四个方法可以在程序运行时决定操作的属性。代码运行之后,决定赋什么属性、什么值。(不是写代码时就写死了,而是运行时可以决定)

    此外,hasattr()还可以避免因为属性没有而导致报错。

    (二).常用的特殊属性

    (1).__name__

    以后web开发中,写"路由"的时候会用到__name__,url配置。

    路径、网络、函数的名字,一一对应。

    返回的是一个字符串。是对象本身的名字,类是类的名字,函数是函数的名字。

    class A:
        pass
    
    
    def bb():
        pass
    
    
    print(A.__name__)  # A
    print(type(A.__name__))  # <class 'str'>
    print(bb.__name__)  # bb
    print(type(bb.__name__))  # <class 'str'>
    View Code

    (2).__class__

    只要有实例,就能得到类。不是类的名称,是类自己本身的对象,而不是名字。

    相当于再得到一个类对象。

    class A:
        pass
    
    
    a = A()
    b = a.__class__
    print(b)  # <class '__main__.A'>

    (3).__doc__

    写代码要有写注释的习惯,不然过了1个星期,你自己都不知道你写的是啥东西了。

    def jiec(n):
        """
        这是一个计算阶乘的递归函数
        :param n: 对哪个数进行阶乘
        :return: 阶乘的结果
        """
        if n == 1:  # 出口
            return 1
        else:
            return jiec(n - 1) * n  # 自己调用自己
    
    
    print(jiec.__doc__)
    """
        这是一个计算阶乘的递归函数
        :param n: 对哪个数进行阶乘
        :return: 阶乘的结果
    """
    
    print(help(jiec))
    """
    Help on function jiec in module __main__:
    
    jiec(n)
        这是一个计算阶乘的递归函数
        :param n: 对哪个数进行阶乘
        :return: 阶乘的结果
    
    None
    """
    View Code

    可以把这个多行注释看作是"文档注释",每个类、方法只能有一个。

    多行注释是存放在__doc__中,返回的是文档字符串,把多行注释返回了。

    (4).__dict__

    查看实例中封装的属性。类中的属性返回的是一个一大串底层的字典格式的对象。

    四、self

    在编写类的代码时,它是常客。

    self它是一个关键字参数,首要参数(这里就记住:必须加),它是标记给哪个类,给每一个实例贴上标签。指的是对象本身。

    如果不加这个self,那么有很多实例的时候,python解释器就分不清是哪一个实例了。python底层是用self来区分每一个实例。

    调用时不需要写self这个参数,python底层已自动为我们实现了,python解释器会自动传递。

    (一).引入魔法方法:__init__()

    先解释一下什么叫魔法方法?被双下划线包裹起来的方法。在特定的情况被触发。就像陷阱,先布置好,一旦有踩到了就触发了这个陷阱。

    阅读如下代码,例1:

    class Account:
        def __init__(self, name, number, money):
            self.name = name
            self.number = number
            self.money = money
    
    a = Account("Tuple", "123456", 8888)  # 实例化
    View Code

    __init__() 初始化的作用,实例化时自动调用(无需手动),初始化属性。用于对象一被创建时,就需要拥有属性的场合,让对象有一些指定的属性。

    例如,一个人一出生就拥有血型、指纹……

    "例1"中 self.name=name 这条在__init__()中的代码,就是让对象有了这个属性。self指对象本身。

    注意:当 __init__() 的括号中有必备参数时,在实例化时,就必须传参数。

    看着上述"例1"的代码,理解下图:(在编写类的代码时,如果不写self,那么实例化的时候,python根本就不知道是哪一个类传过去的)

    (二).其他魔法方法

    (1).__del__() 释放类时被调用。

    python底层有自己的垃圾回收机制。不过当人为 del object 手动删除时,就触发了__del__()。

    这里引入一个例子,此例向我们展示了python被称为高级语言的特性之一:"垃圾回收机制",

    class A:
        def __del__(self):
            print("被销毁了")
    
    
    def func():
        a = A()  # a是在函数调用的时候实例化的
        # 当函数调用结束的时候,里面的对象被销毁
    
    
    func()  # 调用完了都会被销毁
    # 不销毁的话,当很多次调用的时候。a实例在内存中越来越多,最后导致内存溢出,你的电脑瘫痪
    
    """
    运行结果:
    
    被销毁了
    
    为啥?又没去删除过对象,为什么被销毁了?看代码的注释去!
    """
    View Code

    (2).__str__() 向使用者提供尽可能简洁且有用的信息。针对使用者。print()的时候,就是触发了这个魔法方法。

    (3).__repr__() 作用与__str__()很相似。只是这个魔法方法针对开发者,有时用来找BUG的

    (4).str没有的时候,会去找repr

    五、基础OOP的案例

    (一).烤红薯

    """
    小应用:烤红薯
    属性:
        cookedlevel int :
        0-3 表示生的;超过3 表示半生不熟;超过5 表示已经烤好了;超过8 表示烤焦了
        cookedstring string :
        描述红薯的生熟程度
        coodiment : 调料
    行为:
        cook() 把红薯烤一段时间
        addcondiments() 给红薯添加配料
    用到的魔法方法:
    __init__ 设置默认属性
    __str__ 让print的结果更加好看
    """
    
    
    class SweetPotato:
        """这是烤红薯的类"""
    
        # 定义初始化方法
        def __init__(self):
            self.cooklevel = 0
            self.cook_string = "生的"
            self.condiments = []
    
        # 定制print时显示的内容
        def __str__(self):
            msg = self.cook_string + "红薯"
            if len(self.condiments) > 0:
                msg = msg + "("
                for i in self.condiments:
                    msg = msg + i + ","
                msg = msg.strip(",")  # 去掉左右两边的逗号
                msg = msg + ")"
            return msg
    
        # 烤红薯方法
        def cook(self, time):
            self.cooklevel += time
            if self.cooklevel > 8:
                self.cook_string = "烤成灰了"
            elif self.cooklevel > 5:
                self.cook_string = "烤熟了"
            elif self.cooklevel > 3:
                self.cook_string = "半生不熟"
            else:
                self.cook_string = "生的"
    
        # 加调味品
        def add_condiments(self, condiment):
            self.condiments.append(condiment)
    
    
    sp = SweetPotato()  # 实例
    print("有一个红薯还没有烤。", "level:", sp.cooklevel, sp.cook_string, "。调料:", sp.condiments)
    print("接下来开始烤红薯了")
    print("已经烤了4分钟")
    sp.cook(4)
    print(sp)
    print("我又烤了3分钟")
    sp.cook(3)
    print(sp)
    
    print("开始放调料了,我放了芥末")
    sp.add_condiments("芥末")
    print(sp)
    print("我口味比较重,我想放点老干妈")
    sp.add_condiments("老干妈")
    print(sp)
    
    print("我又烤了5分钟")
    sp.cook(5)
    print(sp)
    
    print("烤成灰了也要吃,加杯脉动")
    sp.add_condiments("脉动")
    print(sp)
    
    """
    运行结果:
    
    有一个红薯还没有烤。 level: 0 生的 。调料: []
    接下来开始烤红薯了
    已经烤了4分钟
    半生不熟红薯
    我又烤了3分钟
    烤熟了红薯
    开始放调料了,我放了芥末
    烤熟了红薯(芥末)
    我口味比较重,我想放点老干妈
    烤熟了红薯(芥末,老干妈)
    我又烤了5分钟
    烤成灰了红薯(芥末,老干妈)
    烤成灰了也要吃,加杯脉动
    烤成灰了红薯(芥末,老干妈,脉动)
    """
    View Code

    (二).房间里摆放家具

    """
    你有一个房间,往房间里摆放家具。
    用OOP思想编写代码!
    """
    
    
    class Room:
        def __init__(self, area):
            self.area = area  # 房间的总面积
            self.light = "on"  # 默认让房间的灯是亮着的
            self.contains_item = []  # 家具列表
    
        def __str__(self):
            msg = "当前房间可用面积为:{},".format(str(self.area))
            if len(self.contains_item) > 0:
                # 房间里有家具
                msg = "{}容纳的物品有:{}".format(msg, "".join([i.get_name() for i in self.contains_item]))
                # 预设方法 : get_name() 获取家具的名称
                # self.contains_item中,放的是Bed类的实例,所以可以直接 ".函数名()" 来操作
                # print(self.contains_item)  # [<__main__.Bed object at 0x003E8430>,...]
            return msg
    
        def accommodate_item(self, item):
            # 往房间中添加家具
            need_area = item.get_used_area()  # 预设函数 : 已使用多少面积
            if self.area > need_area:
                # 总面积大于家具的面积,可以往房间里添家具
                self.contains_item.append(item)
                self.area -= need_area  # 家具放进房间了,房间总面被占用了,相应减少可以使用的面积
                print(""{}"已放进房间里了".format(item.get_name()))
            else:
                print("房间面积不够了,塞不进去了!")
    
    
    class Bed:
        """这里以 "床" 为例"""
    
        def __init__(self, area, name="床1"):
            self.area = area  # 床有面积
            self.name = name  # 床有品牌
    
        def __str__(self):
            return "床的面积为:{}".format(str(self.area))
    
        def get_used_area(self):
            return self.area
    
        def get_name(self):
            return self.name
    
    
    room = Room(20)
    print(room)
    b1 = Bed(3)
    print(b1)
    
    room.accommodate_item(b1)  # 往房间里添床了
    # 传过去的是Bed类的实例
    print(room)
    
    print()  # 为了看得清楚
    
    b2 = Bed(5, "席梦思")
    print(b2)
    room.accommodate_item(b2)  # 再添一张床
    print(room)
    
    """
    运行结果:
    
    当前房间可用面积为:20,
    床的面积为:3
    "床1"已放进房间里了
    当前房间可用面积为:17,容纳的物品有:床1
    
    床的面积为:5
    "席梦思"已放进房间里了
    当前房间可用面积为:12,容纳的物品有:床1、席梦思
    """
    View Code

    (三).文字版CS

    """
    文字版 CS
    
    1. 人类
    属性 : 姓名  血量  持有的枪
    方法 : 安子弹  安弹夹  拿枪(持有抢)  开枪
    
    2. 子弹类
    属性 : 杀伤力
    方法 : 伤害敌人(让敌人掉血)
    
    3. 弹夹类
    属性 : 容量(子弹存储的最大值)  当前保存的子弹
    方法 : 保存子弹(安装子弹的时候)  弹出子弹(开枪的时候)
    
    4. 枪类
    属性 : 弹夹(默认没有弹夹,需要安装)
    方法 : 连接弹夹(保存弹夹)  射子弹
    """
    
    
    # 人类
    class Ren:
        # 初始化方法
        def __init__(self, name):
            self.name = name
            self.xue = 100
            self.qiang = None
    
        # 魔术方法
        def __str__(self):
            return self.name + "剩余血量为:" + str(self.xue)
    
        def anzidan(self, danjia, zidan):
            danjia.baocunzidan(zidan)
    
        def andanjia(self, qiang, danjia):
            qiang.lianjiedanjia(danjia)
    
        def naqiang(self, qiang):
            self.qiang = qiang
    
        def kaiqiang(self, diren):
            self.qiang.she(diren)
    
        def diaoxue(self, shashangli):
            self.xue -= shashangli
    
    
    # 弹夹类
    class Danjia:
        def __init__(self, rongliang):
            self.rongliang = rongliang
            self.rongnaList = []
    
        def __str__(self):
            return "弹夹当前的子弹数量为:" + str(len(self.rongnaList)) + "/" + str(self.rongliang)
    
        def baocunzidan(self, zidan):
            if len(self.rongnaList) < self.rongliang:
                self.rongnaList.append(zidan)
    
        def chuzidan(self):
            # 判断当前弹夹中是否还有子弹
            if len(self.rongnaList) > 0:
                # 获取最后压入到单夹中的子弹
                zidan = self.rongnaList[-1]
                # 每一次调用都会用掉一发子弹,所以要删除列表中的最后一个元素
                self.rongnaList.pop()
                return zidan
            else:
                return None
    
    
    # 子弹类
    class Zidan:
        def __init__(self, shashangli):
            self.shashangli = shashangli
    
        def shanghai(self, diren):
            diren.diaoxue(self.shashangli)
    
    
    # 枪类
    class Qiang:
        def __init__(self):
            self.danjia = None
    
        def __str__(self):
            if self.danjia:
                return "M4A1当前有弹夹"
            else:
                return "M4A1当前没有弹夹"
    
        def lianjiedanjia(self, danjia):
            if not self.danjia:
                self.danjia = danjia
    
        def she(self, diren):
            zidan = self.danjia.chuzidan()
            if zidan:
                zidan.shanghai(diren)
            else:
                print("没有子弹了,需要更换弹夹")
    
    
    police = Ren("德国边防第九大队")
    danjia = Danjia(20)
    print(danjia)
    i = 0
    while i < 20:
        zidan = Zidan(20)
        police.anzidan(danjia, zidan)
        i += 1
    print(danjia)
    qiang = Qiang()
    print(qiang)
    police.andanjia(qiang, danjia)
    print(qiang)
    diren = Ren("凤凰战士")
    print(diren)
    police.naqiang(qiang)
    police.kaiqiang(diren)
    print(diren)
    print(danjia)
    police.kaiqiang(diren)
    print(diren)
    print(danjia)
    
    """
    运行结果:
    
    弹夹当前的子弹数量为:0/20
    弹夹当前的子弹数量为:20/20
    M4A1当前没有弹夹
    M4A1当前有弹夹
    凤凰战士剩余血量为:100
    凤凰战士剩余血量为:80
    弹夹当前的子弹数量为:19/20
    凤凰战士剩余血量为:60
    弹夹当前的子弹数量为:18/20
    """
    View Code

    六、继承

    就是相同的类,取他们的共性。(也可以说是派生)。抽象出一个更抽象的类,放公共代码。

    (一).设计思想:

    单继承:从上往下,传统分类思想。

    基于多继承的Mix-in的思路:拼积木,组装。就像拼四驱车,由马达、轴承等等组成一辆完整的四驱车。(代码量一多,拼积木容易把自己绕晕)。一般,Mix-in类是继承的终点。

    (二).继承搜索的顺序

    先找子类 -> 再找父类 -> 最后找object

    注意:不是父类空间的复制。

    寻找多继承的轨迹的方式:__mro__ 属性。

    (三).super()

    用途:当子类需要重写父类时,但还需要用到父类。super()必须加上。

    父类中所有的一切,子类都可以直接拿来用。如果父类无法满足子类的需求,在不改变父类作用的前提下,在子类中重写父类方法时,super()得要加上。

    因为重写了子类,父类中的属性、方法就会被覆盖了,不会再去找父类中的属性、方法了。所以需要super()手动去找一下。

    super()了,后面的__init__()中,self也不用了。

    python解释器会自动去找它的父类。super()是通过mro查找的。

    父类中有多少参数,super().__init__()也得有同样参数。

    super().__init__()拿来的是父类的属性,父类中有哪些属性,都必须要统统写进来。而子类自有的属性就在init中定义。然后传参的时候,就按子类定义的参数传实参就可以了。

    (四).练习:

    定义一个猫咪的父类,再从父类中分出不同种类的猫

    """
    定义一个猫咪的父类,
    再从父类中分出不同种类的猫
    """
    
    
    class Cat:
        """猫咪的父类"""
    
        def __init__(self, color="", weight=10, age=1):
            self.color = color  # 颜色
            self.weight = weight  # 体重
            self.age = age  # 年龄
    
        def __str__(self):
            return "{},体重:{},年龄:{}岁".format(
                self.color, str(self.weight), str(self.age))
    
    
    class GarfieldCat(Cat):
        """加菲猫"""
    
        def __init__(self, color, weight, age, hungry):
            super().__init__(color, weight, age)
            self.hungry = hungry
    
            if self.hungry:
                self.hungry = "我是吃货"
            else:
                self.hungry = "我才不是吃货"
    
        def speak(self):
            print("我是加菲猫,我会说话!")
    
        def __str__(self):
            return "{},体重:{},年龄:{}岁,{}".format(
                self.color, str(self.weight), str(self.age), self.hungry)
    
    
    class TomCat(Cat):
        """TomCat"""
    
        def iam_cute(self):
            print("我是汤姆猫,我会卖萌")
    
    
    garfield = GarfieldCat("棕色", 30, 10, True)
    print(garfield)
    garfield.speak()
    
    print()  # 换行,为了看得清楚
    
    t = TomCat("黑色", 20, 1)
    print(t)
    t.iam_cute()
    
    """
    运行结果:
    
    棕色,体重:30,年龄:10岁,我是吃货
    我是加菲猫,我会说话!
    
    黑色,体重:20,年龄:1岁
    我是汤姆猫,我会卖萌
    """
    View Code

    (五).补充:python不具备多态,python是假多态。(python中是鸭子类型)

    鸭子类型:如果走起路来像鸭子,或者叫起来像鸭子,那就是鸭子!比如那天我学嬴政穿着龙袍,那么认为我就是皇帝。

    (六).广度优先遍历、深度优先遍历。广度:类似就近原则。深度:一条路找到底。类名.mro()就可以看到寻找的轨迹,既不是广度也不是深度,而是C3算法推导出来的。

    广度的寻找轨迹:找BC,再去B里面的DE,最后是object

    深度的寻找轨迹:B -> D -> E -> C

    补充一个案例:(由于冲突导致的不能继承)

    """
    用个生动形象的比喻
    """
    
    
    class A:  # 爸爸
        pass
    
    
    class B:  # 妈妈
        pass
    
    
    class C(A, B):  # 儿子。先继承了爸爸的小JJ
        pass
    
    
    class D(B, A):  # 女儿。先继承了妈妈的...
        pass
    
    
    # 一旦运行下面代码就会报错
    class E(C, D):  # 儿子和女儿能结婚吗?
        pass
    """
    Traceback (most recent call last):
      File "D:/python_local/test1.py", line 21, in <module>
        class E(C, D):
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases A, B
    """
    
    
    """
    C先继承了A 然后B,
    D先继承了B 然后A,
    那么E的寻找轨迹就发生了冲突,到底先找A还是先去找B?然后就报错了。
    C3算法找不到继承轨迹了。
    """
    View Code

    七、装饰器

    装饰器(名词):装饰某个东西。给一个现有的函数增加功能,可以反复使用,你装饰什么函数,那个函数就增加功能。

    装饰(动词):指向了一个新的函数,除了调用原来的函数外,还会做点别的事。函数名还是原来的函数名,指向的却是新的函数,这个新的函数,会调用原来的函数,也可以做些别的事。

    一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数。

    (一).使用装饰器的场景

    (1).提供一个验证功能的时候

    (2).统计函数执行时间

    (3).日志模块,自动打印日志

    (4).执行函数前的预备处理

    (5).函数执行后的一些清理功能

    (二).开放封闭原则

    写代码要遵循 "开放封闭" 的原则。简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展。

    封闭:已实现的功能代码。

    开放:对扩展开放。

    (三).装饰器练习

    一个简单的装饰器,"例1":

    def i_label(fn):
        def add_i():
            return "<i>{}</i>".format(fn())
    
        return add_i
    
    
    def b_label(fn):
        def add_b():
            return "<b>{}</b>".format(fn())
    
        return add_b
    
    
    @i_label
    def sentence1():
        return "第一句话"
        # print("第一句话") <i>None</i>  # print()返回的是None
    
    
    @b_label
    def sentence2():
        return "第二句话"
    
    
    @i_label
    @b_label
    def sentence3():
        return "第三句话"
    
    
    print(sentence1())
    print(sentence2())
    
    print(sentence3())
    """
    先会去执行b_label,执行完后,返回出一个新的函数,这个新函数包含了刚才代码执行后的结果。
    然后再去执行i_label,i_label调用执行的是刚才生成的新函数了,新函数中有b_label执行后的结果,才能拼接在一起。然后返回。
    """
    
    """
    运行结果:
    <i>第一句话</i>
    <b>第二句话</b>
    <i><b>第三句话</b></i>
    """
    View Code

    带参数的装饰器,"例2":

    """
    满足500元的商品可以打9折。
    """
    
    
    def discount(price):
        def exchange(x):  # x是original_price
            if price(x) >= 500:  # price()是调用entirely()
                return price(x) * 0.9
            else:
                return price(x)
    
        return exchange
    
    
    @discount
    def entirely(original_price):
        return original_price
    
    
    print("合计:{}".format(entirely(600)))  # 合计:540.0
    View Code

    大装饰器,"例3":

    import time
    from functools import wraps
    
    """
    装饰器从里到外起作用的
    """
    
    
    def what_you_want(command):
        if command == "get_sum":
            def get_sum(func):
                @wraps(func)
                def sum_inner(*args, **kwargs):
                    values = func(*args, **kwargs)
                    print("打印两数之和:{}".format(sum(values)))
                    return values
    
                return sum_inner
    
            return get_sum
        elif command == "get_cost_time":
            def get_cost_time(func):
                @wraps(func)
                def time_inner(*args, **kwargs):
                    start_time = time.time()
                    time.sleep(0.5)
                    values = func(*args, **kwargs)
                    end_time = time.time()
                    print("耗时{}秒".format(str(end_time - start_time)))
                    return values  # 如果没有返回,get_sum这个装饰器中就会报错
    
                return time_inner
    
            return get_cost_time
    
    
    """
    所有的装饰器装饰好了再运行。
    此案例中,其中一个装饰器需要依赖那个values。
    如果装饰完后没有得到values的返回值,就会报错。
    """
    
    
    @what_you_want("get_sum")  # 如果没有得到values,这条语句必须写再下面语句的后面
    @what_you_want("get_cost_time")
    def my_func(a, b):
        return a, b
    
    
    my_func(10, 20)
    
    """
    运行结果:
    
    耗时0.5000286102294922秒
    打印两数之和:30
    """
    View Code

    由"例3"中可看出:装饰器可以叠加使用,若多个装饰器同时装饰一个函数,那么装饰器的调用顺序和语法糖的声明顺序相反。

    八、描述器

    一个类中,只要有 __get__() 、 __set__() 、__delete__() 这三个魔法方法中的其中一个,那么就是描述器。

    描述器管理一个"类属性"的访问、修改、删除。(上面已有写了)

    基于描述器的装饰器,可以让一个方法,用起来像一个属性。

    class Property:
        """这是一个装饰器,也是一个描述器"""
    
        def __init__(self, func=None):
            self.func = func
    
        def __get__(self, obj, objtype=None):
            if obj is None: return self  # 直接通过类来访问时
            if self.func is None: raise AttributeError("unreadable attribute")
            return self.func(obj)
    
    
    class MyName:
        """想要隐藏自己的昵称,用匿名来代替。如匿名评论"""
    
        def __init__(self, name):
            self.name = name
            self.isanonymous = False  # 是否是匿名的
    
        @Property  # 可以让一个方法,用起来像属性
        def show_name(self):
            if self.isanonymous:  # 如果是匿名的
                return "匿名的"
            else:
                return self.name  # 不是匿名直接返回明文
    
    
    my = MyName("zyb")
    my.isanonymous = True
    print(my.show_name)
    
    """
    运行结果:
    
    匿名的
    """
    View Code

    九、内置装饰器

    (一).@property装饰器

    就是把一个方法,变成属性。就是调用的时候不用加小括号了。

    class A:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        @property
        def foo(self):
            print(self.name)
            return "property foo"
    
    
    p = A("jack", 88)
    print(p.foo)  # 直接就是属性的形式了

    (二).静态方法:@staticmethod,两个看起来都像普通函数。不需要self参数了。

    class A:
        @staticmethod  # 静态方法了
        def meth():
            print("aaa")
            
        # def meth(self):
        #     print("aaa")
    
    
    A.meth()  # 直接用类,就相当于看成普通函数
    
    a = A()
    a.meth()  # 实例调用的时候 --> A.meth(a)
    
    """
    如果我希望:实例调用和类调用都一样,都看成普通函数,怎么办?@staticmethod
    静态方法:会让类和实例,看它都是一个普通函数
    """
    
    """
    运行结果:
    
    aaa
    aaa
    """
    View Code

    (三).类方法:@classmethod,不传实例,只传类。

    class A:
        @classmethod
        def meth(cls):  # 第一个参数是cls 表示类
            print("aaa")
    
    
    A.meth()  # 直接用类,就相当于看成普通函数
    
    a = A()
    a.meth()  # 实例调用的时候 --> A.meth(A) #类当作第一个参数传进去
    
    """
    类方法:第一个传进去的不是实例,而是类。
    
    大概场景:
    以后做数据库接入的时候,有一个ORM,基于面向对象的。
    在查询的时候,可以使用类方法,来简化你的代码。
    """
    View Code

    (四).区别

    (1).@staticmethod 不需要表示自身对象的self参数和自身类的cls参数,就跟使用普通的函数一样。但不加这个装饰器就不可以了!

    (2).@classmethod 不需要self参数,但是第一个cls参数需要表示自身类的cls参数。

    (3).@classmethod的话,在类里的所有方法都可以调用的。

  • 相关阅读:
    PHP中的NULL类型
    js中自定义事件,使用了jQuery
    chrome调试文章
    codeforces 633D
    hdu 1496 Equations
    poj 1286 Necklace of Beads
    poj 2154 Color
    poj 3270 Cow Sorting
    poj 1026 Cipher
    poj 2369 Permutations
  • 原文地址:https://www.cnblogs.com/xuanlv-0413/p/9279044.html
Copyright © 2011-2022 走看看