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

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

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

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


*有点用熬*
*自己敲敲*
posted @ 2019-07-22 00:00  吃夏天的西瓜  阅读(373)  评论(0编辑  收藏  举报