专栏首页极客起源Python编程思想(28):限制类的动态特性(__slots__属性)

Python编程思想(28):限制类的动态特性(__slots__属性)

-----------支持作者请转发本文-----------

李宁老师已经在「极客起源」 微信公众号推出《Python编程思想》电子书,囊括了Python的核心技术,以及Python的主要函数库的使用方法。读者可以在「极客起源」 公众号中输入 160442 开始学习。

-----------正文-----------

Python是动态语言,动态语言的特征之一就是类、对象的属性、方法都可以动态增加和修改。前面已经简单介绍过为对象动态添加属性和方法,本节将进一步介绍 Python的动态特征。

前面介绍了如何为对象动态添加方法,但是所添加的方法只是对当前对象有效,如果希望为所有实例都添加方法,则可通过为类添加方法来实现,代码如下:

示例代码:dynamic_class_method. py

class Dog:
    def __init__(self, name):
        self.name = name
def run(self):
    print(f'{self.name} 正在跑...')
d1 = Dog('土豆')
d2 = Dog('皮皮')
# 为Dog动态添加run()方法,该方法的第一个参数会自动绑定
Dog.run = run
# d1、d2调用walk()方法
d1.run()
d2.run()

上面程序定义了一个Dog类,该Dog类只定义了一个构造方法,并未提供任何其他方法。因此,如果这时调用Dog类的run方法会抛出异常,因为Dog类并没有run方法。

在创建Dog对象后,为Dog类动态添加了run()方法,为Dog动态添加run()方法后,Dog类的两个实例d1hed2都拥有了run()方法,因此上面程序中最后两行d1和d2都可调用run()方法。

Python的这种动态性固然有其优势,但也给程序带来了一定的隐患。原来定义好的类,在任何时候都有可能被其他程序修改,这就带来了一些不确定性。如果程序要限制为某个类动态添加属性和方法,则可以通过__slots__属性来处理。

__slots__属性的值是一个元组,该元组的所有元素列出了该类的实例允许动态添加的所有属性名和方法名(对于 Python而言,方法与属性相同,只是这类属性的值为函数本身),代码如下:

示例代码:slots_demo py

class Dog:
    __slots__ = ('walk', 'age', 'name')
    def __init__(self, name):
        self.name = name
    def run(self):
        print('预定义的run()方法')
d = Dog('Mary')
from types import MethodType
# 只允许动态为实例添加walk、age、name这3个属性或方法
d.walk = MethodType(lambda self: print(f'{self.name}正在慢慢地走'), d)
d.age = 8
# d.sleep = MethodType(lambda self: print(f'{self.name}正在睡觉'), d)  抛出异常
d.walk()

上面程序Dog类中的开始位置通过__slots__= (walk,age,name)现在了Dog类只能动态添加walk、age和name三个属性。因此,这段代码中动态添加的walk和age属性都是允许的,但被注释掉的代码动态添加了sleep属性,就会抛出如下异常:

AttributeError: 'Dog' object has no attribute 'sleep'

需要说明的是, __slots__属性并不限制通过类来动态添加属性或方法,因此下面代码是合法的。

Dog.test = lambda self:print('test') d.test()

下面代码通过Dog类动态添加了test()方法,这样Dog对象就可以调用test()方法了。此外,__slots__属性指定的限制只对当前类的实例起作用,对该类派生出来的子类是不起作用的,代码如下:

class MyDog(Dog):
    def __init__(self, name):
        super().__init__(name)
    pass
md = MyDog('Bill')
# 完全可以为Mydog实例动态添加属性
md.sleep = MethodType(lambda self: print(f'{self.name}正在睡觉'), d)
md.sleep()

从这段代码可以看到,Dog的子类 MyDog的实例完全可以动态添加 sleep属性,这说明__slots__属性指定的限制只对当前类起作用。如果要限制子类的实例动态添加属性和方法,则需要在子类中也定义__ slots__属性,这样,子类的实例允许动态添加属性和方法就是子类的__ slots__元组加上父类的__slots__元组的和,代码如下:

class MyDog(Dog):
    __slots__ = ('sleep')
    def __init__(self, name):
        super().__init__(name)
    pass
md = MyDog('Bill')
# 完全可以为Mydog实例动态添加属性
md.sleep = MethodType(lambda self: print(f'{self.name}正在睡觉'), d)
md.sleep()

md.walk = MethodType(lambda self: print(f'我的 {self.name}正在睡觉'), d)
md.walk()

在这段代码中,子类MyDog只允许添加名为sleep的属性,所以MyDog允许添加的属性是:sleep、walk、age和name。

本文分享自微信公众号 - 极客起源(geekculture),作者:geekori

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python编程思想(30):用 metaclass搞定一批类的特性

    如果希望创建某一批类全部具有某种特征,则可以通过 metaclass来实现。使用 metaclass可以在创建类时动态修改类定义。为了使用 metaclass动...

    蒙娜丽宁
  • Dart语言的接口替代品

    在Dart语言中并没有接口的概念,但接口的功能需要其他功能来弥补,这就是抽象类。接口的作用是用于制定规范。也就是说,在接口中定义的方法,都必须在实现接口的类中实...

    蒙娜丽宁
  • Python编程思想(24):类的实例方法

    对于在类中定义的实例方法,Python会自动绑定方法的第1个参数(通常是self),第1个参数总是指向调用该方法的对象。由于实例方法(包括构造方法)的self参...

    蒙娜丽宁
  • 快捷代码可视化制作怎么设置动画并预览

      我们公司用http协议 用ajax跨域访问会报错 请求错误 no-referrer-when-downgrade。

    要不要吃火锅
  • IT运维可视化效果实现前端就能做?

    CamBuilder 中创建的物体,只有在编辑了 UserID、Name 或者 自定义属性 后,导入到 ThingJS 中才能成为独立的管理对象,被程序读取或修...

    要不要吃火锅
  • IOS系统下虚拟键盘遮挡文本框问题的解决

    最近在项目中发现同样的代码在Android端微信网页中点击文本框唤出的虚拟键盘不会遮挡文本框,但是在IOS端的微信网页中点击文本框唤出的键盘却在大部分情况下会遮...

    lin_zone
  • python 面向对象之类的继承

    AttributeError: 'WoMan' object has no attribute 'go_to_work'

    py3study
  • 通过一个例子学习Kubernetes里的PersistentVolumeClaim的用法

    Kubernetes的pod本身是无状态的(stateless),生命周期通常比较短,只要出现了异常,Kubernetes就会自动创建一个新的Pod来代替它。

    Jerry Wang
  • 类的组合

    py3study
  • 知识图谱(2)——neo4j的用法

    先了解各个命令的用法 创建一个节点 CREATE (ee:Person { name: "Emil", from: "Sweden", klout: 99 ...

    DC童生

扫码关注云+社区

领取腾讯云代金券