Python – 私有化属性方法;Property属性函数;单例;异常;动态添加属性方法;slots(八)

一、私有化属性方法

在Python中,我们可以使用双下划线"__"前缀来创建私有化属性和私有化方法。私有化属性和方法是对象内部的实现细节,外部无法直接访问,但是我们可以通过一些方法来访问或修改这些私有化的属性和方法

1、_x:以_前缀的私有属性/方法/类

以单下划线开头的表示的是 protected 类型的变量

即保护类型只能允许其本身与子类进行访问

不能使用from xxx import * 的方式导入

2、__xx:以__前缀的属性/方法/类

无法在外部直接访问(名字会被重整,所以访问不到),子类中无法继承、访问

如果在子类中向同名属性赋值,会在子类中定义的一个与父类相同名字的属性

3、__xx__:以__前缀和后缀的方法

用户名字空间的魔法对象或属性(如:__init__)

4、xx_:以_为后缀,通常用于避免与 Python 关键词的冲突

(一)私有化属性

语法:两个下划线开头(双下划线"__"前缀),声明该属性为私有,不能在类的外部被使用或直接访问

'''
私有化属性
1、把特定的一个属性隐藏起来,不让类的外部进行直接调用
2、保护这个属性,不让属性的值随意改变
3、保护这个属性,不让派生类【子类】去继承
'''
class Person(object):
    # 私有化类属性
    __hobby = '游泳'
    sex = '男'

    def __init__(self):
        # 加两个下划线__,将此属性私有化之后,就不能在外部直接访问,但在类的内部可以访问
        self.__name = '张晓明'
        self.__age = 18
        self.height = 180
    def get_name(self):
        return self.__name
    def set_name(self,name):
        self.__name = name
    def get_age(self):
        return self.__age
    def set_age(self,age):
        self.__age = age
    def __str__(self):
        return 'name:%s,age:%s' % (self.__name,self.__age)

class Student(Person):
    def print_info(self):
        # 子类也不可以访问父类的私有化属性
        print('姓名:%s,年龄:%s,身高:%s' % (self.__name,self.__age,self.height))
        pass
    pass

xm = Student()
# 类外部无法访问私有化属性;不然报错 AttributeError: 'Student' object has no attribute '__name'
# print(xm.__name)
# 子类也不可以访问父类的私有化属性
# xm.print_info()
# 类内部可以访问私有化属性
print(xm)
# 类外部无法访问私有化属性;不然报错 AttributeError: 'Student' object has no attribute '__hobby'
# print(xm.__hobby)
# AttributeError: type object 'Person' has no attribute '__hobby'
# print(Person.__hobby)

xl = Person()
# 类外部无法访问私有化属性
# print(xl.__name)
print(xl)
'''
总结:
1、私有化的【实例】属性,不能在类外部直接访问,可以在类内部随意访问,修改
2、子类不能继承父类的私有化属性【只能继承父类公共的属性和行为】
3、在属性名的前面直接加‘__’,就可以将其私有化
'''

#name:张晓明,age:18
#name:张晓明,age:18

1、私有化的【实例】属性,不能在类外部直接访问,可以在类内部随意访问,修改

2、子类不能继承父类的私有化属性【只能继承父类公共的属性和行为】 

(二)私有化方法

'''
私有化方法
私有化方法跟私有化属性概念一样,有些重要的方法,不允许外部调用,防止子类意外重写,把普通的方法设置成私有化方法。
语法
私有化方法,即在方法名前面加两个下划线
私有化方法一般是类内部调用,子类不能继承,外部不能调用
'''
class Animal(object):
    def eat(self):
        print('eat')
        self.__drink()
        pass
    def __drink(self):
        print('drink')
        pass
    pass

class Dog(Animal):
    pass

dog = Dog()
dog.eat()
# print('--------外部调用私有化方法--------')
# AttributeError: 'Dog' object has no attribute '__drink'
# dog.__drink()

私有化方法跟私有化属性概念一样,有些重要的方法,不允许外部调用,防止子类意外重写,把普通的方法设置成私有化方法

私有化方法,即在方法名前面加两个下划线

私有化方法一般是类内部调用,子类不能继承,外部不能调用 

二、Property属性函数

property是python中用于定义类属性的一种方法,它允许将方法伪装成属性,使得我们可以像 访问普通属性一样访问方法. 通常用于定义只读属性,或者 通过属性访问来控制属性的设置和获取操作

使用 property 可以帮助你在类中对属性的访问进行更好的控制,而不需要改变属性的访问方式。例如,可以在读取属性时执行一些额外的逻辑,或者在设置属性时进行验证

property 可以通过装饰器或直接调用来实现。下面是两种实现方法。

方法1:使用 property() 函数
class Person(object):
    def __init__(self):
        # 私有化属性
        self.__name = 'xiaoming'
        self.__age = 18
        # self.__height = 180
        # self.__weight = 70
        # self.__sex = '男'
        self.__hobby = '运动'
        self.__job = '程序员'
        # self.__salary = 10000
        # self.__address = '北京市'
        # self.__phone = '13888888888'
        # self.__email = 'xiaoming@163.com'
        # self.__bank_card ='1234567890123456'
        pass

    '''
    方法一:设置访问私有属性方法,通过调用方法访问修改值
    '''
    # 访问私有属性方法
    def get_age(self):
        return self.__age;
    # 修改私有属性方法
    def set_age(self,age):
        self.__age = age
        pass

    '''
    方法二:类属性,即在类中定义值为property对象的类属性
    定义一个属性,当对这个hobby设置值时调用set_hobby,get_hobby 
    注:必须是以get,set开头的方法名,才能被调用
    '''
    def get_hobby(self):
        return self.__hobby
    def set_hobby(self,hobby):
        self.__hobby = hobby
    hobby = property(get_hobby, set_hobby)

    def __str__(self):
        return '姓名:%s 年龄:%d 爱好:%s 职业:%s'%(self.__name,self.__age,self.__hobby,self.__job)

    pass

xm = Person()
print(xm) # 姓名:xiaoming 年龄:18 爱好:运动 职业:程序员
xm.__name = 'xiaohong'
xm.__age = 20
print('外部直接修改私有属性:',xm) # 外部直接修改私有属性: 姓名:xiaoming 年龄:18 爱好:运动 职业:程序员
xm.set_age(20)
print('外部访问修改私有方法:',xm) # 外部访问修改私有方法: 姓名:xiaoming 年龄:20 爱好:运动 职业:程序员
方法2:使用 @property 装饰器

通过装饰器的方式更加简洁和常用

class Person(object):
    def __init__(self):
        # 私有化属性
        self.__name = 'xiaoming'
        self.__age = 18
        # self.__height = 180
        # self.__weight = 70
        # self.__sex = '男'
        self.__hobby = '运动'
        self.__job = '程序员'
        # self.__salary = 10000
        # self.__address = '北京市'
        # self.__phone = '13888888888'
        # self.__email = 'xiaoming@163.com'
        # self.__bank_card ='1234567890123456'
        pass

    '''
    方法三:装饰器,即在方法上使用装饰器
    '''
    # 使用装饰器对age进行装饰,提供一个getter方法
    @property
    def job(self):
        return self.__job
    # 使用装饰器进行装饰,提供一个setter方法
    @job.setter
    def job(self,job):
        self.__job = job

    @job.deleter
    def job(self):
        print("删除 job 属性")
        del self.__job  # 定义 deleter

    def __str__(self):
        return '姓名:%s 年龄:%d 爱好:%s 职业:%s'%(self.__name,self.__age,self.__hobby,self.__job)

    pass

xm = Person()
print(xm) # 姓名:xiaoming 年龄:20 爱好:游泳 职业:程序员
xm.job = '程序员的干活'
print(xm) # 姓名:xiaoming 年龄:20 爱好:游泳 职业:程序员的干活

全部demo代码:

class Person(object):
    def __init__(self):
        # 私有化属性
        self.__name = 'xiaoming'
        self.__age = 18
        # self.__height = 180
        # self.__weight = 70
        # self.__sex = '男'
        self.__hobby = '运动'
        self.__job = '程序员'
        # self.__salary = 10000
        # self.__address = '北京市'
        # self.__phone = '13888888888'
        # self.__email = 'xiaoming@163.com'
        # self.__bank_card ='1234567890123456'
        pass

    '''
    方法一:设置访问私有属性方法,通过调用方法访问修改值
    '''
    # 访问私有属性方法
    def get_age(self):
        return self.__age;
    # 修改私有属性方法
    def set_age(self,age):
        self.__age = age
        pass

    '''
    方法二:类属性,即在类中定义值为property对象的类属性
    定义一个属性,当对这个hobby设置值时调用set_hobby,get_hobby 
    注:必须是以get,set开头的方法名,才能被调用
    '''
    def get_hobby(self):
        return self.__hobby
    def set_hobby(self,hobby):
        self.__hobby = hobby
    hobby = property(get_hobby, set_hobby)

    '''
    方法三:装饰器,即在方法上使用装饰器
    '''
    # 使用装饰器对age进行装饰,提供一个getter方法
    @property
    def job(self):
        return self.__job
    # 使用装饰器进行装饰,提供一个setter方法
    @job.setter
    def job(self,job):
        self.__job = job

    @job.deleter
    def job(self):
        print("删除 job 属性")
        del self.__job  # 定义 deleter

    def __str__(self):
        return '姓名:%s 年龄:%d 爱好:%s 职业:%s'%(self.__name,self.__age,self.__hobby,self.__job)

    pass

xm = Person()
print(xm) # 姓名:xiaoming 年龄:18 爱好:运动 职业:程序员
xm.__name = 'xiaohong'
xm.__age = 20
print('外部直接修改私有属性:',xm) # 外部直接修改私有属性: 姓名:xiaoming 年龄:18 爱好:运动 职业:程序员
xm.set_age(20)
print('外部访问修改私有方法:',xm) # 外部访问修改私有方法: 姓名:xiaoming 年龄:20 爱好:运动 职业:程序员

xm.hobby = '游泳'
print(xm) # 姓名:xiaoming 年龄:20 爱好:游泳 职业:程序员

xm.job = '程序员的干活'
print(xm) # 姓名:xiaoming 年龄:20 爱好:游泳 职业:程序员的干活

三、单例

单例模式‌是一种设计模式,用于确保一个类只有一个实例,并提供一种方式来访问该实例。无论何时创建这个类的对象,都会返回相同的实例。单例模式在需要共享资源或限制某些资源的访问时非常有用‌12。

Python中实现单例模式的方法

  1. 经典模式‌:通过将构造函数私有化,并创建一个静态方法来初始化对象。这样,对象将在第一次调用时创建,此后,这个类将返回同一个对象‌。
  2. 懒汉式‌:在第一次调用时创建对象,之后每次调用都返回同一个对象‌。
  3. 模块级别的单例模式‌:在模块中定义类并实例化,其他文件导入该模块时,导入的对象实际上是同一个实例‌。
  4. 装饰器‌:使用装饰器来简化单例模式的实现,确保类只有一个实例‌。

Python中单例模式的优缺点

优点‌:

  • 全局访问‌:确保只有一个实例,方便全局访问和共享资源。
  • 资源管理‌:可以有效地管理共享资源,如数据库连接、日志记录器等。
  • 线程安全‌:在多线程环境中,单例模式可以确保只有一个实例被创建。
  • 缺点‌:

  • 测试困难‌:由于只有一个实例,测试时可能需要额外的注意,确保测试不会影响到其他测试用例。
  • 滥用风险‌:如果过度使用单例模式,可能会导致代码难以理解和维护。
  • 应用场景

    单例模式常用于以下场景:

  • 配置管理‌:确保全局配置的唯一性。
  • 日志记录‌:确保日志记录器的唯一性。
  • 缓存‌:确保缓存的唯一性。
  • 线程池‌:确保线程池的唯一性‌
  • Python实现单例模式的6种方法

    1、使用模块实现单例

    class Singleton(object):
        def foo(self):
            pass
    singleton = Singleton()
    
    from mysingleton import singleton
    a = singleton
    b = singleton
    print(id(a)) #2642013004064
    print(id(b)) #2642013004064

    2、通过装饰器实现单例

    def singleton_func(cls):
        instance={}
        def _singleton(*args, **kwargs):
            if cls not in instance:
                instance[cls] = cls(*args, **kwargs)
            return instance[cls]
        return _singleton
     
    @singleton_func
    class Phone(object):
        def phone_id(self):
            return id(self)
     
    if __name__ == '__main__':
        p1 = Phone()
        p2 = Phone()
        print(p1.phone_id()) # 1150311080560
        print(p2.phone_id()) # 1150311080560

    在装饰器的内函数中,判断字典是否已经有键值对。如果没有,则添加一个类和类实例的键值对,如果有,则不添加。最后返回字典中的类实例,可以保证每次返回的都是同一个实例。要使用这个单例装饰器,只要将其装饰到需要实现单例的类上即可。

    3、使用实例化方式实现单例

    class SingletonInstance(object):
        def __call__(self, *args, **kwargs):
            return self
    if __name__ == '__main__':
        SingletonInstance = SingletonInstance()
        s1 = SingletonInstance()
        s2 = SingletonInstance()
        print(id(s1)) # 1660087836960
        print(id(s2)) # 1660087836960

    在类中,先重写类的 __call__ 方法,在 __call__ 方法中返回自己。先实例化一个类的对象,后面所有需要使用这个类的地方,都调用这个实例对象。这样,每次调用的都是同一个实例,所以也能实现单例。

    4、使用类装饰器实现单例

    class SingletonDecorator(object):
        _instance = None
        def __init__(self, cls):
            self._cls = cls
     
        def __call__(self, *args, **kwargs):
            if self._instance is None:
                self._instance = self._cls(*args, **kwargs)
            return self._instance
     
    @SingletonDecorator
    class Phone(object):
        def phone_id(self):
            return id(self)
     
    if __name__ == '__main__':
        p1 = Phone()
        p2 = Phone()
        print(p1.phone_id()) # 1718594971232
        print(p2.phone_id()) # 1718594971232

    _var:命名约定,仅供内部使用。通常不会由Python解释器强制执行(私有变量)
    var_:按约定使用以避免与Python关键字的命名冲突
    __var:当在类上下文中使用时,触发”名称修饰“。由Python解释器强制执行
    __var__:表示Python语言定义的特殊方法。避免在你自己的属性中使用这种命名方案

    使用装饰器实现单例,因为装饰器除了可以使用闭包实现,还可以使用类实现,所以也可以使用类装饰器来实现单例。通过重写类的 __call__ 方法实现类装饰器,在 __call__ 方法中判断当前是否有类实例,没有才会创建,从而实现单例。

    5、重写类的__new__方法实现单例(推荐)

    class SingletonClass(object):
        _instance = None
        def __new__(cls, *args, **kwargs):
            # cls._instance=cls.__new__(cls) # 不能使用自身的new方法,容易造成一个深度递归,应该调用父类的new方法
            #if not hasattr(cls, '_instance'):  # 如果不存在就开始创建
            if cls._instance is None:
                cls._instance = object.__new__(cls, *args, **kwargs)
            return cls._instance
    
        _is_init = False
        def __init__(self):
            if not self._is_init:
                print('init')
                self._is_init = True
            pass
    
    if __name__ == '__main__':
        s1 = SingletonClass()
        s2 = SingletonClass()
        print(id(s1)) # 1881376612976
        print(id(s2)) # 1881376612976

    在Python类中,每次实例化一个类对象时,都会自动先执行__new__方法和__init__方法。

    __new__方法先在内存中为实例对象申请空间,然后__init__方法初始化实例对象。因为__init__方法是在new执行完成后自动执行的,每次实例类的对象时都会执行__init__方法,所以也要对__init__方法进行重写,只有第一次实例化类对象时才执行初始化操作。

    通过重写__new__方法,如果这个类没有实例对象,则执行__new__,有则返回已有的实例,从而实现单例。

    6、通过元类(metaclass)实现单例

    元类详解参考Python中的元类

    class SingletonMeta(type, object):
        def __init__(self, *args, **kwargs):
            self._instance = None
            super().__init__(*args, **kwargs)
     
        def __call__(self, *args, **kwargs):
            if self._instance is None:
                self._instance = super().__call__(*args, **kwargs)
            return self._instance
     
    class Phone(object, metaclass=SingletonMeta):
        def phone_id(self):
            return id(self)
     
    if __name__ == '__main__':
        p1 = Phone()
        p2 = Phone()
        print(p1.phone_id()) # 2198314411280
        print(p2.phone_id()) # 2198314411280

    四、异常和错误

    Python 中(至少)有两种错误:语法错误和异常(syntax errors 和 exceptions)

    (一)错误

    语法错误,也被称作解析错误,无法通过python解释器的语法检测,必须在程序执行前就改正。比如:

    >>> if True print('Hello world')
      File "<stdin>", line 1, in ?
        if True print('Hello world')
                ^^^^^
    SyntaxError: invalid syntax

    语法分析器指出错误行,并且在检测到错误的位置前面显示一个小“箭头”。 错误是由箭头 前面 的标记引起的(或者至少是这么检测的): 这个例子中,函数 print() 被发现存在错误,因为它前面少了一个冒号( ':' )。 错误会输出文件名和行号,所以如果是从脚本输入的你就知道去哪里检查错误了。

    (二)异常

    异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。异常是Python对象,表示一个错误。即使一条语句或表达式在语法上是正确的,当试图执行它时也可能会引发错误。运行期检测到的错误称为 异常,并且程序不会无条件的崩溃。所以我们需要异常的处理来解决这些问题。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

    异常也有不同的类型,异常类型做为错误信息的一部分显示出来:零除错误( ZeroDivisionError) ,命名错误( NameError) 和 类型错误( TypeError )。打印错误信息时,异常的类型作为异常的内置名显示。对于所有的内置异常都是如此,不过用户自定义异常就不一定了(尽管这是一个很有用的约定)。标准异常名是内置的标识(没有保留关键字)。后一部分是关于该异常类型的详细说明,这意味着它的内容依赖于异常类型。错误信息的前半部分以堆栈的形式列出异常发生的位置。通常在堆栈中列出了源代码行,然而,来自标准输入的源码不会显示出来。

    python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。

    1、异常捕获和处理(try/except/finally语句块)

    2、断言(Assertions

    如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(traceback)终止执行。

    python2.x捕获异常语法(只能py2):

    try:
        ...some functions...
    except Exception, e:
        print(e)

    python3.x捕获异常语法(py2也可以,推荐使用,有兼容能力,性能更好):

    try:
        ...some functions...
    except Exception as e:
        print(e)

    异常捕获语法格式:

    try: 
        可能出现错误的代码块 
    except: 
        出错之后执行的代码块 
    else: 
        没有出错的代码块 
    finally: 
        不管有没有出错都执行的代码块 
    

    Python中内置异常类型

    异常            导致的错误
    AssertionError    当assert语句失败时引发。
    AttributeError    当属性分配或引用失败时引发。
    EOFError    当input()函数达到文件结束条件时引发。
    FloatingPointError    当浮点运算失败时引发。
    GeneratorExit    调用生成器的close()方法时引发。
    ImportError    找不到导入的模块时引发。
    IndexError    当序列的索引超出范围时引发。
    KeyError    在字典中找不到键时引发。
    KeyboardInterrupt    当用户按下中断键(Ctrl+c或delete)时引发。
    MemoryError    当操作耗尽内存时引发。
    NameError    在局部或全局范围内找不到变量时引发。
    NotImplementedError    由抽象方法提出。
    OSError    当系统操作导致系统相关错误时引发。
    OverflowError    当算术运算的结果太大而无法表示时引发。
    ReferenceError    使用弱引用代理访问垃圾收集的引用时引发。
    RuntimeError    当错误不属于任何其他类别时引发。
    StopIteration    函数引发,以指示迭代器不再返回任何项。
    SyntaxError    遇到语法错误时由解析器引发。
    IndentationError    当缩进不正确时引发。
    TabError    当缩进由不一致的制表符和空格组成时引发。
    SystemError    当解释器检测到内部错误时引发。
    SystemExit    由sys.exit()函数引发。
    TypeError    将函数或操作应用于类型不正确的对象时引发。
    UnboundLocalError    当在函数或方法中引用局部变量,但没有将值绑定到该变量时引发。
    UnicodeError    当发生与unicode相关的编码或解码错误时引发。
    UnicodeEncodeError    当编码过程中发生与unicode相关的错误时引发。
    UnicodeDecodeError    当解码过程中出现与unicode相关的错误时引发。
    UnicodeTranslateError    翻译过程中发生与unicode相关的错误时引发。
    ValueError    当函数得到类型正确但值不正确的参数时引发。
    ZeroDivisionError    当除法或模运算的第二个操作数为零时引发 

    # if True print('Hello world')
    
    # import Exception
    # except 在捕获错误异常的时候  只要根据具体的错误类型来捕获的
    # 用一个块 可以捕获多个不同类型的异常
    # Exception 可以捕获所有的异常  当对出现的问题或者错误不确定的情况下  可以使用Exception
    # print(dir(Exception))
    try:
        print(b) #捕获逻辑的代码
        li=[1,2,34]
        print(li[10])  #通过下标去访问列表
        a=10/0
        pass
    # except  NameError as msg:
    #     # 捕获到的错误 才会在这里执行
    #     print(msg)
    #     pass
    # except IndexError as msg:
    #     print(msg)
    #     pass
    # except ZeroDivisionError as msg:
    #     print(msg)
    #     pass
    except Exception as result:
        # 在此尽量的去处理捕获到的错误
        print(result)
        pass
    # print('初次接触异常处理')
    
    def A(s):
        return 10/int(s)
        pass
    def B(s):
        return A(s)*2
    def main():
        # B(0)
        try:
            B('0')
            pass
        except Exception as msg:
            print(msg)
            pass
        pass
    
    '''
    main()
    不需要在每个可能出错的地方去捕获,只要在合适的层次去捕获错误就可以, 这样就大大减少写try---except
    异常的抛出机制
    如果在运行时发生异常,解释器会查找相应的异常捕获类型
    如果在当前函数里面没有找到,它会将异常传递给上层的调用函数,看能否处理
    如果在最外层 没有找到的话,解释器就会退出,程序down掉
    '''
    
    print('----------------try-except-else----------------')
    # try-except-else
    try:
        print('我是没有错误产生的')
        pass
    except SyntaxError as msg:
        print(msg)
    except Exception as msg:
        print('error',msg)
    else:
        print('当Try里面的代码 没有出现异常的情况下 我才会执行')
        pass
    
    print('----------------try-except-finally----------------')
    # try-except-finally
    try:
        int('3')
        open('aa.txt')
    except Exception as msg:
        print(msg)
        pass
    finally:
        print('释放文件的资源、数据库连接是资源等等')
        print('不管有没有出错都执行的代码块')
    
    
    输出:
    name 'b' is not defined
    ----------------try-except-else----------------
    我是没有错误产生的
    当Try里面的代码 没有出现异常的情况下 我才会执行
    ----------------try-except-finally----------------
    [Errno 2] No such file or directory: 'aa.txt'
    释放文件的资源、数据库连接是资源等等
    不管有没有出错都执行的代码块
    

    自定义异常

    自定义异常,都要直接或间接继承Error或Exception类。 由开发者主动抛出自定义异常,在python中使用raise关键字

    # 自定义一个异常类
    class LeNumExcept(Exception):  # 自定义异常类需要继承Exception
        def __str__(self):
            return '[error:]你输入的数字小于0,请出入大于0的数字'
        pass
    
    try:
        num = int(input('请出入一个数字:'))
        if num < 0:
        # raise 关键字抛出异常
            raise LeNumExcept()
    except LeNumExcept as e:
        print(e)  # 捕获异常
    else:
        print('没有异常')

    五、动态添加属性方法

    动态语言:运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化

    1.1 动态语言

  • 在运行时代码可以根据某些条件改变自身结构
  • 可以在运行时引进新的函数、对象、甚至代码,可以删除已有的函数等其他结构上的变化
  • 常见的动态语言:JavaScript、PHP、Python、Erlang
  • 1.2 动态类型语言

  • 在运行期间检查数据类型的语言
  • 数据类型不是在编译阶段决定的,而是把类型绑定延后到了运行阶段
  • 常见的动态类型语言:Python、Ruby、Erlang、JavaScript、swift、PHP、Perl
  • 1.3 强类型语言

  • 一旦一个变量被指定了某个数据类型,如果不经过强制类型转换,那么它就永远是这个数据类型
  • 常见的强类型语言:Java、C#、Python、Object-C、Ruby
  • Python是动态语言,动态类型语言,也是强类型语言。所以Python可以在运行时改变自身结构,动态添加/删除属性和方法。接下来将介绍Python如何动态添加属性和方法

    # 1、动态添加属性
    class Student(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
            pass
    
        def __str__(self):
            return '姓名:%s 年龄:%d'%(self.name, self.age)
            pass
    
    xm = Student('xiaoming', 18)
    # 动态绑定hobby属性,给对象添加属性
    xm.hobby = '运动'
    print(xm.hobby) # 运动
    print(xm) # 姓名:xiaoming 年龄:18
    
    # 添加类属性
    Student.school = '上海交大'
    print(Student.school) # 上海交大
    
    #2、动态添加方法
    # 动态添加实例方法需要使用types
    # 添加方法的库
    import types
    
    def dymic_method(self):
        print('{}的年纪是:{},爱好:{}'.format(self.name, self.age, Student.hobby))
        pass
    
    @classmethod
    def class_test(cls):
        print('这是一个类方法')
        pass
    
    @staticmethod
    def static_method_test():
        print('这是一个静态方法')
        pass
    
    # 动态添加实例方法
    xm.dymic_methocd = types.MethodType(dymic_method, xm)
    Student.hobby = '游泳'
    xm.dymic_methocd() # xiaoming的年纪是:18,爱好:游泳
    
    # 动态添加类方法
    Student.class_method = class_test
    Student.class_method() # 这是一个类方法
    
    # 动态添加静态方法
    Student.static_method = static_method_test
    Student.static_method() # 这是一个静态方法

    动态添加实例方法需要使用types

    给类绑定类方法和静态方法;使用方式:类名.方法名 = xxxx 

    六、slots

    __slots__ 是 Python 类中的一个特殊属性,用于限制实例可以动态添加的属性,并减少内存占用

    python是动态语言,在运行的时候可以动态添加属性。如果要限制在运行的时候给类添加属性,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。

    只有在__slots__变量中的属性才能被添加,没有在__slots__变量中的属性会添加失败。可以防止其他人在调用类的时候胡乱添加属性或方法。__slots__属性子类不会继承,只有在当前类中有效

    1、没有__slots__ 情况:

    当类没有定义 __slots__ 时,每个实例会有一个默认的字典(__dict__)来存储对象的属性
    意味着可以动态添加属性,并且每个实例的内存使用会稍微多一些,因为需要存储字典

    特点:

    灵活性高,可以动态添加属性
    由于使用字典存储属性,内存占用较多
    属性访问速度较慢,因为字典查找涉及哈希计算

    2、有__slots__ 的情况:

    不再为每个实例创建 __dict__,而是直接使用一种更紧凑的数据结构来存储属性

    这可以显著减少内存使用,但限制了动态添加属性

    特点:

    节省内存,因为不需要为每个实例存储字典
    限制动态添加未在 __slots__ 中定义的属性
    属性访问速度更快,因为直接引用而非字典查找

    基本的总结如表所示:

    特性 没有 __slots__ 有 __slots__
    内存使用 较高,使用字典存储属性 较低,使用更紧凑的数据结构
    动态添加属性 允许 不允许,除非在 slots 中定义
    属性访问速度 较慢 较快
    灵活性
    __dict__ 属性是否存在 存在 不存在
    使用场景 需要动态添加属性的类 内存优化和确定属性的类

    继承关系当中的使用__slots__

    (1)子类未声明__slots__时,不会继承父类的__slots__,此时子类是可以随意给属性赋值

    (2)子类声明了__slots__时,会继承父类的__slots__(子类__slots__的范围是其自身+父类的__slots__)

    '''
    slots限制要添加的实例属性,节约内存空间
    '''
    class Student(object):
        __slots__ = ('name', 'age')
        def __str__(self):
            return '姓名:%s 年龄:%d' % (self.name, self.age)
            pass
    
    xm = Student()
    xm.name = 'xiaoming'
    xm.age = 18
    # 如果__slots__中没有的属性,则添加实例属性会报错
    # AttributeError: 'Student' object has no attribute 'hobby' and no __dict__ for setting new attributes
    # xm.hobby = '游泳'
    print(xm)
    # __dict__:所有可用属性都在这里存储,缺点:占用内存空间大
    # print(xm.__dict__)
    
    '''
    继承关系当中的使用__slots__
    子类未声明__slots__时,不会继承父类的__slots__,此时子类是可以随意给属性赋值
    子类声明了__slots__时,会继承父类的__slots__(子类__slots__的范围是其自身+父类的__slots__)
    '''
    class SubStudent(Student):
        __slots__ = ('score', 'hobby')
        pass
    
    xh = SubStudent()
    xh.name = '小红'

    作者:MinggeQingchun

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python – 私有化属性方法;Property属性函数;单例;异常;动态添加属性方法;slots(八)

    发表回复