• (二十一)类与对象 ---- 三个装饰器


    静态属性

    我们知道类既有函数属性(即方法属性)又有数据属性,实例只有数据属性,我们在使用实例调用类的函数属性并运行时,总要带上函数后面的括号才能运行,不然总是调用函数的内存地址

    class room:                                         #一个房子的类
        def __init__(self,width,length,high):
            self.width = width
            self.length = length
            self.high = high
        def cal_tiji(self):                             #函数属性,求房子的体积
            return self.width*self.length*self.high
    
    r1 = room(3,5,6)
    print(r1.cal_tiji)                #<bound method room.cal_tiji of <__main__.room object at 0x0000020610CF1C18>>
    print(r1.cal_tiji())              #90

    我们如何能像调用数据属性一样调用函数属性呢?

    类中提供了@property关键字,可以看成@property是一个装饰器,装饰器的作用是调用类的函数属性时,直接运行该函数。像是调用类的属性一样来直接调用并运行类的函数,具体操作如:

    class room:
        def __init__(self,width,length,high):
            self.width = width
            self.length = length
            self.high = high
        @property                                     #property关键字,其他的地方都不用改
        def cal_tiji(self):
            return self.width*self.length*self.high
    
    r1 = room(3,5,6)
    print(r1.cal_tiji)                                #像调用数据属性一样调用函数属性
    print(r1.width)
    结果:90 3

    静态属性:把函数属性封装成数据属性的样子被调用

    类方法

    如果要求,不进行实例化,直接调用类的函数,此时会提示缺少必要的位置参数self

    class room:
        addr = '万里大厦'
        def __init__(self,num):
            self.num = num
    
        def show_addr(self):
            print(room.addr)
    
    room.show_addr()
    结果: TypeError: show_addr() missing 1 required positional argument: 'self'

    虽然我们可以随意加上一个位置参数,但是注意到此处的self有特殊含义,它是指实例的本身,也就是说要使用self必须要先实例化才行。

    class room:
        addr = '万里大厦'
        def __init__(self,num):
            self.num = num
    
        def show_addr(self):
            print(room.addr)
    r1 = room(302)               #为了self参数,只能先实例化一个对象r1
    room.show_addr(r1)
    结果:万里大厦

    难道调用类的函数属性一定要先实例化一个对象,直接调用类的函数能不能不要和实例绑定?

    类中提供了@classmethod装饰器,说明这个函数是专门提供给类调用的,可以直接通过类来调用类的函数,与实例无关

    和函数属性有一个默认的self参数一样,类方法也有一个默认的参数cls,这个cls就是类本身

    class room:
        addr = '万里大厦'
        def __init__(self,num):
            self.num = num
    
        @classmethod                 #classmethod关键字,表示这是一个类方法,专门提供给类调用
        def show_addr(cls):
            print(cls)               #<class '__main__.room'>   类本身
            print(cls.addr)          #万里大厦
    
    room.show_addr()                 #不用传位置参数,和函数的self一样,自动帮我们传了

    类方法的使用场景:类级别的操作,专门给类自己去调用自己的方法,与实例没有任何关系

    静态方法

    静态属性(@property)中位置参数是self,说明与实例绑定

    类方法(@classmethod)中位置参数是cls,说明与类绑定

    如果要求:在类中定义一个函数,要求该函数中的位置函数与实例无关,与所在的类本身也无关。

    为了解决这个问题,类中引入了@staticmethod装饰器

    class room:
        addr = '万里大厦'
        def __init__(self,num):
            self.num = num
    
        @staticmethod               #staticmethod关键字,表示这是一个静态方法,与类无关,与实例也无关
        def test(a,b):         #位置参数中既没有self,也没有cls
            print(a,b)
    
    r1 = room('304')
    room.test(1,2)                  #用类可以调用
    r1.test(1,2)                    #用实例也可以调用
    结果:

    1 2
    1 2

    可以看出,虽然test方法与实例无关,与类本身也无关,但是却可以通过实例和类来调用它,且使用实例调用它时不会传入实例本身的位置参数(在正常类方法中,实例化类后,实例在调用它时,会自动默认首先传入实例本身即self)

    到了此处可能有人问,为什么不能直接在类中定义一个函数,不传self形参?按照上面的提议是否能满足实例可以调用,类本身也可以调用的要求呢?

    class room:
        addr = '万里大厦'
        def __init__(self,num):
            self.num = num
    
        def test(a,b):               #直接在类中定义一个函数,不传self参数
            print(a,b)
    
    room.test(1,2)                   #1 2
    r1 = room('304')     
    r1.test(1,2)                     #TypeError: test() takes 2 positional arguments but 3 were given

    如上,如果直接在类中定义一个常规方法(不含self的形参),是可以通过类本身访问它。但是通过实例来访问它时,虽然也是传入2个参数,但是实际上python自动默认首位传入了self,这样就造成了上面的情况了(传的是2个参数,收到的是3个参数),所以这种方式无法满足上述的需求

    总结:静态方法只是名义上的归属类管理,静态方法中不能调用类属性和实例属性(因为没有self也没有cls),是类的工具包

    静态方法的使用场景:处理和类和实例都无关的逻辑操作


    静态属性中参数有self,所以静态属性中可以调用类属性和实例属性

    类方法中参数有cls,所以类方法中可以调用类属性

    静态方法中没有self和cls,所以静态方法中不能调用类属性和实例属性


    类的关联组合

    类和类没有共同点(没有相同的属性),但是相互之间却有关联

    比如学校、老师、课程这三者,都有各自独立的属性,相互之间又有一定的联系:老师属于学校,课程属于老师,所以老师有学校这个属性,课程又有老师这个属性

    class School:                            #学校类
        def __init__(self,name):
            self.name = name
    
    class Teacher:
        def __init__(self,name,school):      #老师类,有学校这个属性
            self.name = name
            self.school = school
    
    class Course:
        def __init__(self,name,teacher):     #课程类,有老师这个属性
            self.name = name
    #        self.school = school
            self.teacher = teacher
    
    s1 = School('清华')
    s2 = School('北大')
    
    t1 = Teacher('张三',s1)                   #学校的实例作为参数传给老师类去实例化
    t2 = Teacher('李四',s2)
    
    c1 = Course('语文',t1)                    #老师的实例作为参数传给课程类去实例化
    c2 = Course('数学',t2)
    
    msg = '''
    1 语文
    2 数学
    '''
    while True:
        print(msg)
        dic = {
            '1':c1,
            '2':c2,
        }
        choice = input('请选择课程:')
        course_obj = dic[choice]
        print('你选择的课程是%s,老师是%s,学校是%s' %(course_obj.name,course_obj.teacher.name,course_obj.teacher.school.name))            #一步步向上访问实例属性
    结果:

    1 语文
    2 数学

    请选择课程:1
    你选择的课程是语文,老师是张三,学校是清华

  • 相关阅读:
    Object类中常见的方法,为什么wait notify会放在Object里边
    什么是Java序列化和反序列化,如何实现Java序列化
    hashCode和equals方法的区别与联系
    MVC设计思想
    dstat命令--检查linux系统性能
    linux mint19.1解决网易云音乐安装后打不开的问题
    linux 修改用户密码
    MD5加密
    xmpp
    cocoapods安装
  • 原文地址:https://www.cnblogs.com/xulan0922/p/10336350.html
Copyright © 2020-2023  润新知