• ~~面向对象进阶(一):类方法~~


    进击の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方法

      所以到此为止,我们知道了如何将方法变成一个静态的变量

      并对这个静态的方法进行操作!


    *有点用熬*
    *自己敲敲*
  • 相关阅读:
    基于Networks of Brokers的HA方案
    淘宝开源任务调度框架tbschedule
    java.lang.ClassCastException: org.springframework.web.filter.CharacterEncodingFilter cannot be cast to javax.servlet.Filter
    spring boot1.3.0版本及以上版本profile指定参数无法被打入
    diffuse linux 文件比对工具
    PipedInputStream/PipedOutputStream原理
    应聘华为 16道经典面试题及回答思路
    MySql Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 解决方法
    查看linux下某端口被哪个进程占用(linux命令)
    Python3判断自身脚本是不是在运行
  • 原文地址:https://www.cnblogs.com/jevious/p/11258926.html
Copyright © 2020-2023  润新知