[Python]属性的使用

阅读: 评论:0

[Python]属性的使用

[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@propertydef radius(self):return self._radius@radius.setterdef radius(self, radius):self._radius = radius if radius > 0 else 0if __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@propertydef radius(self):return self._radius@radius.setterdef 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 = nameself._password = ''   # 密文存储@propertydef password(self):return decryption(self._password)  # 解密@password.setterdef 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 = brandself._max_speed = max_speed@propertydef brand(self):return self._brand@brand.setterdef brand(self, brand):self._brand = brand@propertydef max_speed(self):return self._max_speed@max_speed.setterdef max_speed(self, max_speed):if max_speed < 0:raise ValueError('Invalid max speed for car')self._max_speed = max_speeddef __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 = brandself._max_speed = max_speed@propertydef brand(self):return self._brand@brand.setterdef brand(self, brand):self._brand = brand@brand.deleterdef brand(self):del self._brandprint('已删除brand')@propertydef max_speed(self):return self._max_speed@max_speed.setterdef max_speed(self, max_speed):if max_speed < 0:raise ValueError('Invalid max speed for car')self._max_speed = max_speeddef __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=3def __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 = brandself._max_speed = max_speed@propertydef brand(self):return self._brand@brand.setterdef brand(self, brand):self._brand = brand@brand.deleterdef brand(self):del self._brandprint('已删除brand')@propertydef max_speed(self):return self._max_speed@max_speed.setterdef max_speed(self, max_speed):if max_speed < 0:raise ValueError('Invalid max speed for car')self._max_speed = max_speeddef __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._branddef set_brand(self, brand):self._brand = branddef get_max_speed(self):return self._max_speeddef set_max_speed(self, max_speed):if max_speed < 0:raise ValueError('Invalid max speed for car')self._max_speed = max_speeddef __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 = hourself._minute = minuteself._second = second@classmethod#这里第一个参数是cls, 表示调用当前的类名def now(cls):ctime = localtime(time())#返回用获取的时间初始化的类return _hour, _min, _sec)def run(self):"""走字"""self._second += 1if self._second == 60:self._second = 0self._minute += 1if self._minute == 60:self._minute = 0self._hour += 1if self._hour == 24:self._hour = 0def show(self):"""显示时间"""return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)

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

调用:

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

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

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

(六)@abstractmethod

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

from abc import ABCMeta, abstractmethod
class Employee(object, metaclass=ABCMeta):def __init__(self, name):self._name = name@propertydef name(self):return self._name@abstractmethoddef get_salary(self):passclass Manager(Employee):# 想一想: 如果不定义构造方法会怎么样def __init__(self, name):# 想一想: 如果不调用父类构造器会怎么样super().__init__(name)def get_salary(self):return 12000class Programmer(Employee):def __init__(self, name):super().__init__(name)def set_working_hour(self, working_hour):self._working_hour = working_hourdef get_salary(self):return 100 * self._working_hourclass Salesman(Employee):def __init__(self, name):super().__init__(name)def set_sales(self, sales):self._sales = salesdef get_salary(self):return 1500 + self._sales * 0.05if __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, _salary()))

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

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

(七)@staticmethod

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

使用场景

  • 在类中实现一些与类相关的操作,但不需要访问类的属性和方法时。
  • 定义一个与类无关的辅助函数,但又不想将其定义在类之外。
from math import sqrt
class Triangle(object):def __init__(self, a, b, c):self._a = aself._b = bself._c = c# 静态方法@staticmethoddef 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._cdef area(self):p = self.perimeter() / 2return 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('不能构成三角形.')

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

本文发布于:2024-01-31 02:36:32,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170663979324728.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:属性   Python
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23