进击のpython
面向对象进阶-类方法
-
classmethod 类方法
python的类方法是通过@classmethod装饰器实现的
类方法和普通的方法的区别是
类方法只能访问类变量,不能访问实例变量
class Dog(object): def __init__(self, name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name) d = Dog("Mjj") d.eat()
可以看到我调用Dog里面的eat方法,显示报错
AttributeError: type object 'Dog' has no attribute 'name'
果然,加上了这个类方法之后就没有办法访问实例变量(name)
但是她说可以访问类的变量,我们来定义一下
class Dog(object): name = "我是类变量" def __init__(self, name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name) d = Dog("Mjj") d.eat()
执行结果如下:
我是类变量 is eating
熬 就可以看出来,使用了类变量之后
真的就只能调用类变量,而无法调用实例变量
但是为什么只能调用类变量不能调用实例变量呢?
我们打印一下self看一下传过来的是什么
class Dog(object): name = "我是类变量" def __init__(self, name): self.name = name @classmethod def eat(self): print(self) print("%s is eating" % self.name) d = Dog("Mjj") d.eat()
<class '__main__.Dog'> 我是类变量 is eating
可以看到,这个传进来的是个类的本身
而不是实例的对象
所以当然就无法调用实例的属性了呀!
而且你也发现了,当你先@classmethod
再进行方法的定义的时候
你的括号里自动补充的不是self而是cls
那到底有什么用呢?
我在别人写的代码上有的时候会看到
但是在我自己进行开发的时候,基本上没用到
但是还是要给你举个例子,让你看看作用
现有一个Student类, 每生成一个Student实例,学生总数就自动+1
class Student(object): stu_num = 0 # 学员计数需存在类变量里,不能存在每个实例里,因为每个实例的内存是独立的,每实例化一次,数据就会被重置一次,不会累加 def __init__(self, name): self.name = name self.stu_num += 1 print("total student num:", self.stu_num) s1 = Student("张1") s2 = Student("张2") s3 = Student("张3") s4 = Student("张4")
执行结果如下
total student num: 1 total student num: 1 total student num: 1 total student num: 1
为何每次都是1呢? 不应该累加么?
self.stu_num += 1
相当于对每个实例进行了一次赋值, 因为self代表实例本身
self.stu_num += 1 相当于s1.stu_num =1 , s2.stu_num = 1 …
把这句代码改成如下就可以:
class Student(object): stu_num = 0 # 学员计数需存在类变量里,不能存在每个实例里,因为每个实例的内存是独立的,每实例化一次,数据就会被重置一次,不会累加 def __init__(self, name): self.name = name Student.stu_num += 1 # 直接用类名调用 print("total student num:", self.stu_num) s1 = Student("张1") s2 = Student("张2") s3 = Student("张3") s4 = Student("张4")
由于stu_num是每增加一个学员才加1的,不应该允许直接Student.stu_num+=1,不安全
可以封装成私有变量,再通过类方法调用并计数
class Student(object): __stu_num = 0 # 学员计数需存在类变量里,不能存在每个实例里 def __init__(self, name): self.name = name self.add_stu_num() # 相当于Student.add_stu_num() 初始化学员时调用 @classmethod def add_stu_num(cls): # 注意这里调用时传进来的其实是类本身,不是实例本身,所以参数名一般改为cls cls.__stu_num += 1 print("total student num:", cls.__stu_num) s1 = Student("张1") s2 = Student("张2") s3 = Student("张3") s4 = Student("张4")
但是有的同学说!
Student.add_stu_num()
也是可以调的,你这样写是为了防止作弊
我这也能作弊啊!
你说的没错!
那么我们如何才能真的判断是不是真的要执行这句话?
关键点是不是在于我是否真的产生了一个类对象所以,在执行之前就要有个验证
def __init__(self, name): self.name = name self.add_stu_num(self) def add_stu_num(cls,obj): if obj.name: cls.__stu_num += 1 print("total student num:", cls.__stu_num)
这里我们用了obj
obj stands for self instance
这里的obj就是实例的意思
那经过这个操作之后,再进行“作弊操作”,就不行了!
-
staticmethod 静态方法
类方法不能访问实例变量,只能访问类变量是吧
静态方法更狠!
不仅实例变量,连类变量都不能访问!
!!!!!!!!!!!!!!!!!!!
@staticmethod装饰器即可把其装饰的方法变为一个静态方法
什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用
并且在方法里可以通过self.调用实例变量或类变量
但静态方法是不可以访问实例变量或类变量的
一个不能访问实例变量和类变量的方法
其实相当于跟类本身已经没什么关系了
它与类唯一的关联就是需要通过类名来调用这个方法
class Student(object): role = "stu" def __init__(self, name): self.name = name @staticmethod def fly(self): print("%s is flying..." % self.name) s = Student("hah") s.fly()
TypeError: fly() missing 1 required positional argument: 'self'
可以看到,并没有在调用fly方法的时候,默认添加进去一个参数
而是报了一个缺少一个参数的错误,一个实例对象
而当我把实例对象传进去,就没有问题了
s = Student("hah") s.fly(s)
hah is flying...
其实你也就看出来了
这个装饰器,将fly方法,和Student类的关系隔离了
就算你在我的类下,你也不能调用我的方法
不常用
-
property 属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性
class Student(object): stu_num = 0 def __init__(self, name): self.name = name @property def fly(self): print("%s is flying..." % self.name) s = Student("haha") s.fly()
直接这么调用会给你返回一个错误:
TypeError: 'NoneType' object is not callable
因为此时fly已经从方法变成属性了
而不再是一个方法了
那么怎么调用属性呢?
实例.属性 是吧
s.fly
执行出来就是这种效果
haha is flying...
理解起来很简单
那么到底有什么用呢?
如果我想要一个静态变量
我直接定义就好了
为什么要这么做呢?
首先,定义一个静态变量可以,但是只是个变量
他除了存储数据,没有别的卵用
但是通过装饰器@property是可以把方法变成静态的
而方法是可以做事情(调用)的
那在现实场景,有什么用呢?
比如我们要找火车票
是不是会先给服务器发信息
然后返回状态,最后打印啊
那么问题就来了
你在查询票的时候,你会关注他到底是怎么拿到数据的吗?
不会吧,而是会更多的关注票的信息
那你在获得票的信息的时候是不是想查询的是票的属性
而不是怎么获得的票!
那我们就可以模拟的写一下!
class Train(object): def __init__(self, name): self.train_name = name def checking_status(self): print("connecting trainline company api...... ") print("checking train %s status " % self.train_name) return 1 @property def train_status(self): status = self.checking_status() if status == 0: print("train got canceled...") elif status == 1: print("train is arrived...") elif status == 2: print("train has departured already...") else: print("cannot confirm the train status...,please check later") f = Train("k470") f.train_status
这样,用户在使用的时候就直接查询了票的属性
骚操作来了奥!既然他是个静态属性,是不是也是属性
那我能不能修改呢?
f.train_status = 0
AttributeError: can't set attribute
┗|`O′|┛ 嗷~~,会报错!
其实也能理解
你只是看上去是在查看属性
但是实际上,你还是在使用调用方法
所以不能修改也是合情合理
但是毕竟是个属性,如果用户提出修改的要求也无可厚非
对吧,你给我个属性,然后你说不能改?
说不过去吧!
所以这种问题怎么处理呢?
class Train(object): def __init__(self, name): self.train_name = name def checking_status(self): print("connecting airline company api...... ") print("checking train %s status " % self.train_name) return 1 @property def train_status(self): status = self.checking_status() if status == 0: print("train got canceled...") elif status == 1: print("train is arrived...") elif status == 2: print("train has departured already...") else: print("cannot confirm the train status...,please check later") @train_status.setter def train_status(self, status): print("changing... ...", status) f = Train("k470") f.train_status = 0
在下面再写一个同名的函数,然后在上面加一个装饰器
名字是这个同名的函数加上.setter方法
同时要把你要修改的参数传进去(status)
changing... ... 0
那到底是不是真的修改了呢?
我们可以设置一个变量来接收
然后打印看看
@train_status.setter def train_status(self, status): print("changing... ...", status) self.status = status f = Train("k470") f.train_status = 0 print(f.status)
打印了一下,澳!确实是有所改变
那我们能删除嘛?
del f.train_status
可以看到,报错了是吧
那我就想删除怎么办呢?
一样的!
在下面再写一个同名的函数,然后在上面加一个装饰器
名字是这个同名的函数加上.deleter方法
所以到此为止,我们知道了如何将方法变成一个静态的变量
并对这个静态的方法进行操作!