静态属性
我们知道类既有函数属性(即方法属性)又有数据属性,实例只有数据属性,我们在使用实例调用类的函数属性并运行时,总要带上函数后面的括号才能运行,不然总是调用函数的内存地址
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
你选择的课程是语文,老师是张三,学校是清华