zoukankan      html  css  js  c++  java
  • 面向对象--类成员

    声明:内容转至 https://www.cnblogs.com/z-joshua/p/6386596.html,谢谢共享!

    类的成员可以分为三大类:字段、方法和属性:

    注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。

    (一)字段

    字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同

    复制代码
     1 class Province:
     2     # 静态字段
     3     country = "China"
     4 
     5     def __init__(self, name):
     6         # 普通字段
     7         self.name = name
     8 
     9 obj_henan = Province("HeNan")
    10 
    11 print(obj_hunan.name) # 使用对象访问跑一趟那个字段
    12 print(Province.country) # 使用类访问静态字段
    复制代码

    由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:

    由上图可是:

    • 静态字段在内存中只保存一份
    • 普通字段在每个对象中都要保存一份

    应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

    (二)方法

    方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。

    • 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
    • 类方法:由调用; 至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls
    • 静态方法:由调用;无默认参数;
    复制代码
     1 class Foo:
     2     def __init__(self, name):
     3         self.name = name
     4 
     5     # 普通方法,由对象调用
     6     def ord_fun(self):
     7         print(self.name)
     8 
     9     # 静态方法
    10     # 普通方法经过两步变成静态字段,1,去掉参数self,2,加上装饰器 @staticmethod
    11     # 如果类里的方法用不到对象(self)里的字段,静态方法可以有除了self的参数
    12     # 静态方法其实就是面向过程里的普通函数,之所以放到类里就是表示方法和该类有关
    13     @staticmethod
    14     def static_func(arg1):
    15         print(arg1)
    16 
    17     # 类方法
    18     # 静态方法的一种特殊形式,自带特殊参数cls,使用装饰器 @classmethod
    19     @classmethod
    20     def class_func(cls): # cls = class
    21         print(cls)
    22 
    23 obj_foo = Foo("foo object")
    24 obj_foo.ord_fun()
    25 Foo.static_func("static func")
    26 Foo.class_func()
    复制代码

    相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

    不同点:方法调用者不同、调用方法时自动传入的参数不同。

    (三) 属性

    为了理解Python的属性,我们先来看一个例子,

    假如我们有一个学生类:

    1 class Student(object):
    2     def __init__(self):
    3         self.score = 0
    4 
    5 s1 = Student()
    6 s1.score = 9999

    这个赋值 s1.score = 9999 这显然不合逻辑,score的正常取值范围应该是0-100, 为了限制score的范围,我们可能会通过定义一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:

    复制代码
     1 class Student(object):
     2     def __init__(self):
     3         pass 4 
     5     def set_score(self, value):
     6         if not isinstance(value, int):
     7             raise ValueError("Score must be int type")
     8         elif score < 0 or value> 100:
     9             raise ValueError("Score must between 0 - 100")
    10         else:
    11             self.score = value
    12 
    13 s1 = Student()
    14 s1.set_score(9999)
    复制代码

    这时 s1.set_score(9999) 就会有如下的报错:

    1     raise ValueError("Score must between 0 - 100")
    2 ValueError: Score must between 0 - 100

    但是,上面的调用方法又略显复杂

    有没有既能检查参数,又可以类似于访问字段一样设置score的方法呢?(使用score = 9999,而不用s1.set_score(9999)), 对于追求完美的Python程序员来说,这是必须要做到的!

    还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的:

    于是我们对上面的Studeng类做修改:

    复制代码
     1 class Student(object):
     2     def __init__(self):
     3         pass
     4 
     5     @property
     6     def score(self, score):
     7         return self.score
     8 
     9     @score.setter
    10     def score(self, value): # 函数名和@property一致
    11         if not isinstance(value, int):
    12             raise ValueError("Score must be int type")
    13         elif value < 0 or value > 100:
    14             raise ValueError("Score must between 0 - 100")
    15         else:
    16             self.score = value
    17 
    18 s1 = Student()
    19 s1.score = 9999 # 这里用 "="访问score
    复制代码

    @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

    1 raise ValueError("Score must between 0 - 100")
    2 ValueError: Score must between 0 - 100

    注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

    还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

    复制代码
     1     @property
     2     def birth(self):
     3         return self._birth
     4 
     5     @birth.setter
     6     def birth(self, value):
     7         self._birth = value
     8 
     9     @property
    10     def age(self):
    11         return 2017 - self._birth
    复制代码

    上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。

    @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

  • 相关阅读:
    如何学习Java基础
    什么阻碍手动测试发挥价值
    API自动化测试指南
    筛选自动化测试用例的技巧
    拷贝HttpRequestBase对象
    测试自动化的边缘DevTestOps
    2019年浏览器市场份额排行榜
    CentOS7 通过 YUM 升级 VIM8
    vim配置go语法高亮
    打开程序总是会提示 "Enter password to unlock your login keyring"
  • 原文地址:https://www.cnblogs.com/weigege2015/p/7865885.html
Copyright © 2011-2022 走看看