[Python]属性的使用- 访问器/修改器/删除器、静态方法/类方法

目录

(一)@property、@*.setter装饰器用法

(二)删除器@*.deleter

(三)使用__slots__对属性加以限制

(四)使用已有方法定义访问器/修改器/删除器

(五)@classmethod

(六)@abstractmethod

(七)@staticmethod


在Python中,如果我们仅仅使用property()函数为属性设置装饰器,那么该属性是只读的,不可以被修改。

如果,我们需要为类属性添加一个可写的装饰器,那么我们需要使用 @propertyname.setter 来修饰类的属性,但此时类的属性是可以被删除的。

如果,我们需要为类属性添加一个删除的装饰器,那么我们需要使用 @propertyname.deleter 来修饰类的属性。

(一)@property、@*.setter装饰器用法

在数据的读取和存入前常常需要对数据进行预处理,通过@property和@*.setter两个装饰器就可以方便的实现。

@property装饰器可以总结为两个作用:

  1. 让函数可以像普通变量一样使用
  2. 对要读取的数据进行预处理

@*.setter装饰器可以总结为两个作用:

  1. 在数据从外部存入类属性前对数据进行预处理
  2. 设置可读属性(不可修改)

  注意:@*.setter装饰器必须在@property装饰器的后面,且两个被修饰的函数的名称必须保持一致,* 即为函数名称。

class Circle(object):
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, radius):
        self._radius = radius if radius > 0 else 0

if __name__ == '__main__':  
    radius = float(input('请输入游泳池的半径: '))
    small = Circle(radius)

 一、@property作用

不加@property时我们可以使用small.radius()调用radius,但 radius更像是small实例的一个属性,而非它的一个函数,使用@property装饰器我们就看可以使用small的属性方式来得到radius

print(small.radius)

对于radius来说使用类属性 small.radius 的方式获取会比 small.radius() 更加的合理。 

二、@*.setter作用

1)数据存入small.radiu前先对其进行处理再存入,实例中大于0则存入类属性_radius中,实现在存入数据之前对数据进行处理。

2)设置只读属性,即无法对属性进行修改。

class Circle(object):
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, radius):
        print('不能修改')

if __name__ == '__main__':  
    radius = float(input('请输入游泳池的半径: '))
    small = Circle(radius)
    print(small.radius)

在为radius赋值时会立即触发@radius.setter装饰器下的radius函数中的提示语句或者异常处理语句。

综合实例

通过@*.setter和@property的组合使用我们就可以实现密码的密文存储和明文输出,具体步骤为:用户输入明文->转化为密文后存入->用户读取时先转化为明文再输出。 

class User():
    def __init__(self, name):
        self.name = name
        self._password = ''   # 密文存储

    @property
    def password(self):
        return decryption(self._password)  # 解密
    @password.setter
    def password(self,word):
        self._password = encryption(word)  # 加密

user = User('xiao')
user.password = '123'   #明文输入
print(user.password)    #明文输出

总结:为两个同名函数打上@*.setter装饰器和@property装饰器后,当把函数作为变量赋值时会触发@*.setter对应的函数,当把函数作为变量读取时会触发@property对应的函数,因此我们可以将其用于数据的预处理。

(二)删除器@*.deleter

class Car(object):
     __slots__ = ('_brand', '_max_speed')

    def __init__(self, brand, max_speed):
        self._brand = brand
        self._max_speed = max_speed

    @property
    def brand(self):
        return self._brand

    @brand.setter
    def brand(self, brand):
        self._brand = brand

    @property
    def max_speed(self):
        return self._max_speed

    @max_speed.setter
    def max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)

car = Car('QQ', 120)
del car.brand

未添加删除器运行del 属性时:

 添加删除器:

class Car(object):
    def __init__(self, brand, max_speed):
        self._brand = brand
        self._max_speed = max_speed

    @property
    def brand(self):
        return self._brand

    @brand.setter
    def brand(self, brand):
        self._brand = brand

    @brand.deleter
    def brand(self):
        del self._brand
        print('已删除brand')

    @property
    def max_speed(self):
        return self._max_speed

    @max_speed.setter
    def max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)

car = Car('QQ', 120)
del car.brand

成功将类属性删除。

(三)使用__slots__对属性加以限制

在python新式类中,可以定义一个变量__slots__,它的作用是阻止在实例化类时为实例分配dict

默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性,举例如下:

class Car(object):
    val=3
    def __init__(self):
        pass
car = Car()
car.x=2
print(car.__dict__)

dict只保存实例中变量,对于类的属性是不保存的,类的属性包括变量和函数。
由于每次实例化一个类都要分配一个新的dict,存在空间的浪费,因此有了__slots__。__slots__是一个元组,包括了当前能访问到的属性。
当定义了slots后,slots中定义的变量变成了类的描述符,相当于java,c++中的成员变量声明,类的实例只能拥有slots中定义的变量,不能再增加新的变量。注意:定义了slots后,就不再有dict。

class Car(object):

    __slots__ = ('_brand', '_max_speed')

    def __init__(self, brand, max_speed):
        self._brand = brand
        self._max_speed = max_speed

    @property
    def brand(self):
        return self._brand

    @brand.setter
    def brand(self, brand):
        self._brand = brand

    @brand.deleter
    def brand(self):
        del self._brand
        print('已删除brand')

    @property
    def max_speed(self):
        return self._max_speed

    @max_speed.setter
    def max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)

car = Car('QQ', 120)
print(car)
car.current_speed = 80

 

 不允许设置除了slot中变量以外的其他实例变量。

(四)使用已有方法定义访问器/修改器/删除器

属性全部由函数定义,最后用property()封装为属性brand的方法。

class Car(object):

    def __init__(self, brand, max_speed):
        self.set_brand(brand)
        self.set_max_speed(max_speed)

    def get_brand(self):
        return self._brand

    def set_brand(self, brand):
        self._brand = brand

    def get_max_speed(self):
        return self._max_speed

    def set_max_speed(self, max_speed):
        if max_speed < 0:
            raise ValueError('Invalid max speed for car')
        self._max_speed = max_speed

    def __str__(self):
        return 'Car: [品牌=%s, 最高时速=%d]' % (self._brand, self._max_speed)

    # 用已有的修改器和访问器定义属性
    brand = property(get_brand, set_brand)
    max_speed = property(get_max_speed, set_max_speed)

property() 函数
语法:
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
说明:
fget 是获取属性值的方法。
fset 是设置属性值的方法。
fdel 是删除属性值的方法。
doc 是属性描述信息。如果省略,会把 fget 方法的 docstring 拿来用(如果有的话)

brand = property(get_brand, set_brand,del_brand)

表示当获取属性值时执行函数get_brand的代码,当设置属性值时执行函数set_brand的代码,当删除属性值时执行函数del_brand的代码。

(五)@classmethod

from time import time, localtime, sleep
class Clock(object):
    """数字时钟"""
    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod
    #这里第一个参数是cls, 表示调用当前的类名
    def now(cls):
        ctime = localtime(time())
        #返回用获取的时间初始化的类
        return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)

 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。

调用:

clock = Clock.now()
    while True:
        print(clock.show())
        sleep(1)
        clock.run()

调用的时候相当于先调用clock.now()得到localtime(time())当前本地时间,然后才用当前本地时间作为参数对Clock类进行初始化。

@classmethod也可用作对输入先进行处理之后在对处理之后的数据创建类。这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。

(六)@abstractmethod

有时,我们抽象出一个基类,知道要有哪些方法,但并不实现功能,只能继承,而不能被实例化,但子类必须要实现该方法,要用到抽象基类。

from abc import ABCMeta, abstractmethod
class Employee(object, metaclass=ABCMeta):

    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @abstractmethod
    def get_salary(self):
        pass

class Manager(Employee):

    # 想一想: 如果不定义构造方法会怎么样
    def __init__(self, name):
        # 想一想: 如果不调用父类构造器会怎么样
        super().__init__(name)

    def get_salary(self):
        return 12000

class Programmer(Employee):

    def __init__(self, name):
        super().__init__(name)

    def set_working_hour(self, working_hour):
        self._working_hour = working_hour

    def get_salary(self):
        return 100 * self._working_hour

class Salesman(Employee):

    def __init__(self, name):
        super().__init__(name)

    def set_sales(self, sales):
        self._sales = sales

    def get_salary(self):
        return 1500 + self._sales * 0.05

if __name__ == '__main__':
    emps = [Manager('武则天'), Programmer('狄仁杰'), Salesman('白元芳')]
    for emp in emps:
        if isinstance(emp, Programmer):
            working_hour = int(input('请输入%s本月工作时间: ' % emp.name))
            emp.set_working_hour(working_hour)
        elif isinstance(emp, Salesman):
            sales = float(input('请输入%s本月销售额: ' % emp.name))
            emp.set_sales(sales)
        print('%s本月月薪为: ¥%.2f元' % (emp.name, emp.get_salary()))

抽象基类只能继承而不能实例化,子类要实例化必须先实现该方法。

例子中定义职工基类,要有读取salary的功能,在子类manager、programmer、salesman中分别重写该方法,这三个子类才能够被实例化,否则不能创建实例化对象。

(七)@staticmethod

而它所装饰的方法既不和实例化对象绑定,也不和类绑定所以它既不能调用实例化对象的属性,也不能使用类的属性。

使用场景

  • 在类中实现一些与类相关的操作,但不需要访问类的属性和方法时。
  • 定义一个与类无关的辅助函数,但又不想将其定义在类之外。
from math import sqrt
class Triangle(object):
    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c
    # 静态方法
    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and c + a > b
    # 实例方法
    def perimeter(self):
        return self._a + self._b + self._c
    def area(self):
        p = self.perimeter() / 2
        return sqrt(p * (p - self._a) * (p - self._b) * (p - self._c))
if __name__ == '__main__':
    # 用字符串的split方法将字符串拆分成一个列表,再通过map函数对列表中的每个字符串进行映射处理成小数
    a, b, c = map(float, input('请输入三条边: ').split())
    # 先判断给定长度的三条边能否构成三角形,如果能才创建三角形对象
    if Triangle.is_valid(a, b, c):
        tri = Triangle(a, b, c)
        print('周长:', tri.perimeter())
        print('面积:', tri.area())
        # 如果传入对象作为方法参数也可以通过类调用实例方法
        print('周长:', Triangle.perimeter(tri))
        print('面积:', Triangle.area(tri))
        # 看看下面的代码就知道其实二者本质上是一致的
        print(type(tri.perimeter))
        print(type(Triangle.perimeter))
    else:
        print('不能构成三角形.')

调用是使用类名.方法名(参数…)调用,当然也可以使用对象进行调用(本质上一样,因为对象调用会先在对象的字典中找方法,找不到再去类的字典中找)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值