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

静态属性

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

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,说明与实例绑定

扫描二维码关注公众号,回复: 5129595 查看本文章

类方法(@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
你选择的课程是语文,老师是张三,学校是清华

猜你喜欢

转载自www.cnblogs.com/xulan0922/p/10336350.html