• python中类与对象的命名空间(静态属性的陷阱)、__dict__ 和 dir() 在继承中使用说明


    1. 面向对象的概念

      1)类是一类抽象的事物,对象是一个具体的事物;用类创建对象的过程,称为实例化。

      2)类就是一个模子,只知道在这个模子里有什么属性、什么方法,但是不知道这些属性、方法具体是什么;

        所以,我们要在这个模子的基础上 造出一个具体的实例(对象),这个实例就会具体化属性、方法

      3)所有的数据类型都是类,都是抽象的;根据数据类型定义变量,该变量就是一个具体的值(对象)。

    面向过程 --> 面向对象的转变

      定义一个函数   《==》 定义一个类

      函数的返回值(字典)   《==》类的对象(类似字典,只不过调用方式发生了改变)

      函数的执行过程 《==》类的实例化

      请看 代码展示1 和 代码展示2

     1 # 用面向对象的思想、面向过程的语法 去实现 Rectangle的计算
     2 def Rectangle(length=0, width=0):
     3     self = {}  # 存储属性值
     4     def __init__(*args):  # 初始化函数 -- 完成对矩阵rectangle的长、宽初始化,以及面积、周长的调用方式
     5         self['length'] = args[0]
     6         self['width'] = args[1]
     7         self['area'] = area
     8         self['perimeter'] = perimeter
     9     def area():
    10         return self['length'] * self['width']
    11     def perimeter():
    12         return 2 * (self['length'] + self['width'])
    13     __init__(length, width)  # 调用初始化函数
    14     return self
    15 
    16 rec_obj = Rectangle(10,5)   # rec_obj 相当于类的一个实例
    17 print(rec_obj)      # rec_obj中存放了实例的属性、方法,通过实例可以查看属性 与 调用方法
    18 print('length:%s, width:%s, area:%s, perimeter:%s'\
    19       %(rec_obj['length'], rec_obj['width'], rec_obj['area'](), rec_obj['perimeter']()))
    代码展示1
     1 class Rectangle:
     2     temVar = 'over' #定义静态属性,共享于类中的每个对象
     3     def __init__(self, *args):  # 创建对象后执行的第一个函数,self就是类创建的对象,该函数返回类的对象self
     4         self.length = args[0]
     5         self.width = args[1]
     6     def area(self):
     7         return self.length * self.width
     8     def perimeter(self):
     9         return 2 * (self.length + self.width)
    10 
    11 rec_obj1 = Rectangle(10, 5) # 实例化一个具体对象
    12 
    13 # 通过 对象 查看属性(包括静态属性)与调用方法
    14 print('length:%s, width:%s, area:%s, perimeter:%s'\
    15       %(rec_obj1.length, rec_obj1.width, rec_obj1.area(), rec_obj1.perimeter()))
    16 print(rec_obj1.temVar)  # 静态属性
    17 
    18 # 通过 类名 调用方法 、类中的静态属性
    19 print('area:%s, perimeter:%s'%( Rectangle.area(rec_obj1), Rectangle.perimeter(rec_obj1)))
    20 print(Rectangle.temVar)  # 静态属性
    21 
    22 # 通过对象名修改属性(若self里 存在该属性,是修改;若self里 不存在该属性,是添加新属性)
    23 # rec_obj1.length = 20
    24 # rec_obj1.temVar = 'object_over'  # 给对象中添加一个新属性 'temVar': 'object_over'
    25 # 通过类名修改属性(若类里 存在该属性<静态属性>,是修改;若类里 不存在该属性,是添加新属性,<静态属性>)
    26 # Rectangle.length = 50   # 在类中添加一个新属性 'length': 50
    27 # Rectangle.temVar = 'class_over'
    28 
    29 # __dict__的使用
    30 print(rec_obj1.__dict__)    # 查看对象的所有属性,即self属性
    31 print(Rectangle.__dict__)   # 查看类的所有静态属性、方法
    32 # __dict__ 对于 对象的 增删改查操作都可以通过字典的语法进行
    33 # __dict__ 对于 类中的名字只能看 不能操作
    代码展示2
    22 # 通过对象名修改属性(若self里 存在该属性,是修改;若self里 不存在该属性,是添加新属性)
    23 # rec_obj1.length = 20
    24 # rec_obj1.temVar = 'object_over' # 给对象中添加一个新属性 'temVar': 'object_over'
    25 # 通过类名修改属性(若类里 存在该属性<静态属性>,是修改;若类里 不存在该属性,是添加新属性,<静态属性>)
    26 # Rectangle.length = 50 # 在类中添加一个新属性 'length': 50
    27 # Rectangle.temVar = 'class_over'

    总结:

    # 对象 = 类名()

    # 实例化的过程:

      # 类名() -> 会创造出一个对象,即创建了一个self变量

        # 调用__init__(self)方法,类名括号里的参数会被这里接收

        # 执行__init__(self)方法

        # 返回self

    # 对象能做的事:

      # 查看属性(自己的属性 和 类中静态属性)

      # 调用方法

      # __dict__ 对于对象的增删改查操作都可以通过字典的语法进行

    # 类名能做的事:

       # 实例化

       # 调用类中的属性,也就是调用静态属性  

       # 调用方法 : 只不过要自己传递self参数

       # __dict__ 对于类中的名字只能看 不能操作

    2. 类与对象的关系(类与对象的命名空间问题)

    (1)类的命名空间

      创建一个类,就会自动创建一个该类的命名空间,在该命名空间中存储类的属性(静态属性、动态属性(方法));

      静态属性:直接在类中定义的变量;(静态属性属于类,即属于所有对象)

      动态属性:在类中定义的函数;(动态属性绑定到所有对象)

    (2)对象的命名空间

      实例化一个对象,就会自动创建一个该对象的命名空间,在该命名空间中存放对象的属性;同时,在实例化之后,就后产生一个指向类对象指针,用来指向当前对象所属类的命名空间,这样就可以访问类的静态属性与动态属性。

      在对象寻找属性的过程中,优先从对象的命名空间中搜索,然后去类的命名空间中查找,最后在父类的命名空间中查找...,若没有找到该属性,程序就会抛出异常。

      注:类与对象的命名空间是独立存储的

     完整代码展示:

    复制代码
    class Family:
        '''
        定义一个公共账号 ,只要有人上班,就将钱存到这个账号上
        '''
        share_money = 0  # 不可变数据类型做静态属性
        native_place = ['china']    # 可变数据类型做静态属性
        def __init__(self, role, name, salary):
            self.role = role
            self.name = name
            self.salary = salary
    
        def work(self):
            Family.share_money += self.salary   # 将每个的钱都存放到这个公共账号上
            print('the account remains ¥%s '%Family.share_money)
    
    member1 = Family('father', 'lilei', 1000)
    member2 = Family('mother', 'zhanghua', 500)
    member1.work()  # the account remains ¥1000
    member2.work()  # the account remains ¥1500
    member1.share_money = 200   # 为自己独立开了个小金库,并存入200元   -- 在对象member1中添加这一属性
    member1.share_money += 100  # 以后就可以在自己的小金库中存放私房钱,即总金额=200+100=300
    member2.share_money += 400  # 将公有账号作为自己的私有账号,并存入400元,即总金额=1000+500+400=1900
    print(Family.share_money)   # 1000+500=1500
    print(member1.share_money)  # 200+100=300
    print(member2.share_money)  # 1000+500+400=1900
    
    """
    可变数据类型做静态属性的影响:
    Family.native_place = 'america'
    # member1.native_place[0] = 'america' # 修改的是类中的native_place,会影响所有对象(同上)
    # member2.native_place[0] = 'america' # 修改的是类中的native_place,会影响所有对象(同上)
    print(member1.__dict__) 
    print(member2.__dict__)
    print(Family.__dict__)
    
    {'role': 'father', 'name': 'lilei', 'salary': 1000, 'share_money': 300}
    {'role': 'mother', 'name': 'zhanghua', 'salary': 500, 'share_money': 1900}
    {'__module__': '__main__', '__doc__': '\n    定义一个公共账号 ,只要有人上班,就将钱存到这个账号上\n    ', 
    'share_money': 1500, 'native_place': ['america'], '__init__': , 
    'work': , '__dict__': , 
    '__weakref__': }
    """
    
    """
    可变数据类型做静态属性的影响:
    member1.native_place = 'america'    # 重新赋值,在当前对象的命名空间中添加这个属性,不会影响其它对象
    print(member1.__dict__) 
    print(member2.__dict__)
    print(Family.__dict__)
    
    {'role': 'father', 'name': 'lilei', 'salary': 1000, 'share_money': 300, 'native_place': 'america'}
    {'role': 'mother', 'name': 'zhanghua', 'salary': 500, 'share_money': 1900}
    {'__module__': '__main__', '__doc__': '\n    定义一个公共账号 ,只要有人上班,就将钱存到这个账号上\n    ', 
    'share_money': 1500, 'native_place': ['china'], '__init__': , 
    'work': , '__dict__': , 
    '__weakref__': }
    """
    复制代码
    
    

    对类中静态属性访问规则:

    (1)对于不可变数据类型来说,最好用类名操作静态属性;
      若用对象名操作静态属性,其修改 和 重新赋值 都是独立的(独立的:对象与类的命名空间分开存放)
       1)若用对象名第一次修改静态属性,首先会到类的命名空间中找到该静态属性的属性值,然后在当前对象的命名空间中再做修改
          2)若用对象名直接给静态属性重新赋值,那么直接会在当前对象的命名空间中添加这一属性
    (2)对于可变数据类型来说,用对象名修改是 共享的, 用对象名重新赋值是 独立的
      因为修改的是指针变量所指向内存中的值,故是 共享的
      !!!总结操作静态属性,最好用类名操作静态属性;

    补充:python中不可变数据类型可变数据类型

      不可变数据类型:对于相同的值对应的内存地址是不变的;

    复制代码
     1 a = 1
     2 b = 1
     3 c = 2
     4 d = a + b
     5 print(" id(a) = %d\n id(b) = %d\n id(c) = %d\n id(d) = %d\n"
     6       % (id(a), id(b), id(c), id(d)))
     7 
     8 """
     9  id(a) = 1461563616
    10  id(b) = 1461563616
    11  id(c) = 1461563648
    12  id(d) = 1461563648
    13 """
    复制代码

      可变的数据类型:对于相同值的内存地址是可变的;

    复制代码
     1 al = [1, 2, 3]
     2 bl = [1, 2, 3]
     3 print(" id(al) = %d\n id(bl) = %d\n" % (id(al), id(bl)))
     4 al.append(4)
     5 bl += [4]
     6 print(" id(al) = %d\n id(bl) = %d\n" % (id(al), id(bl)))
     7 print(" al:%s\n bl:%s\n" % (al, bl))
     8 
     9 """
    10  id(al) = 2353965003720
    11  id(bl) = 2353964735816
    12 
    13  id(al) = 2353965003720
    14  id(bl) = 2353964735816
    15 
    16  al:[1, 2, 3, 4]
    17  bl:[1, 2, 3, 4]
    18 """
    复制代码

    类属性的补充

    复制代码
    一:我们定义的类的属性到底存到哪里了?有两种方式查看
    dir(类名):查出的是一个名字列表
    类名.__dict__:查出的是一个字典,key为属性名,value为属性值
    
    二:特殊的类属性
    类名.__name__   # 类的名字(字符串)
    类名.__doc__    # 类的文档字符串
    类名.__base__   # 类的第一个父类
    类名.__bases__  # 类所有父类构成的元组
    类名.__dict__   # 类的字典属性
    类名.__module__ # 类定义所在的模块
    类名.__class__  # 实例对应的类(仅新式类中)
    复制代码

     3 __dict__ 与 dir() 的使用

    复制代码
    """
    对象名.__dict__:查看对象的属性(self对象中存储的变量)
    类名.__dict__:查看类的属性(在类中能看到的静态属性与动态属性)
    
    dir(对象名):查看对象的所有属性(此时包括self对象、类属性、内置方法)
    dir(类名):查看类的所有属性(不包括self对象)
    
    注:在继承中,子类名.__dict__中看不到父类中的类属性,但实际上包括父类的类属性
    """
    复制代码

    案例分析

    复制代码
    class Family:
        '''
        定义一个公共账号 ,只要有人上班,就将钱存到这个账号上
        '''
        share_money = 0  # 不可变数据类型做静态属性
        native_place = ['china']    # 可变数据类型做静态属性
        def __init__(self, role, name, salary):
            self.role = role
            self.name = name
            self.salary = salary
    
        def work(self):
            Family.share_money += self.salary   # 将每个人的钱都存放到这个公共账号上
            print('the account remains ¥%s '%Family.share_money)
    
        def fun(self):
            pass
    
    class NewFamily(Family):
        new_account = 0
        def __init__(self, role, name, salary, kind):
            super(NewFamily, self).__init__(role, name, salary)
            self.kind = kind
    
        def work(self):
            pass
    
        # 使用__定义私有属性
        # python中不存在严格的私有属性,在类的外部可通过正真的函数名【_类名__函数名,即 _NewFamily__expenditure】间接调用
        def __expenditure(self):
            pass
    
    
    f = Family('father', 'lilei', 1000)
    nf = NewFamily("son", "liwei", 2000, "salesman")
    
    print("-"*20, "nf.__dict__ 与 f.__dict__ 对比", "-"*20)
    print(f.__dict__)
    print(nf.__dict__)
    print(set(nf.__dict__) - set(f.__dict__))
    
    print("-"*20, "NewFamily.__dict__ 与 Family.__dict__ 对比", "-"*20)
    print(Family.__dict__)
    print(NewFamily.__dict__)
    print(set(NewFamily.__dict__) - set(Family.__dict__))
    
    print("-"*20, "dir(nf) 与 dir(f) 对比", "-"*20)
    print(dir(f))
    print(dir(nf))
    print(set(dir(nf)) - set(dir(f)))
    
    print("-"*20, "dir(NewFamily) 与 dir(Family) 对比", "-"*20)
    print(dir(Family))
    print(dir(NewFamily))
    print(set(dir(NewFamily)) - set(dir(Family)))
    复制代码

    输出结果:

    复制代码
    """
    -------------------- nf.__dict__ 与 f.__dict__ 对比 --------------------
    {'role': 'father', 'name': 'lilei', 'salary': 1000}
    {'role': 'son', 'name': 'liwei', 'salary': 2000, 'kind': 'salesman'}
    {'kind'}
    -------------------- NewFamily.__dict__ 与 Family.__dict__ 对比 --------------------
    {'__module__': '__main__', '__doc__': '\n    定义一个公共账号 ,只要有人上班,就将钱存到这个账号上\n    ', 'share_money': 0, 'native_place': ['china'], '__init__': , 'work': , 'fun': , '__dict__': , '__weakref__': }
    {'__module__': '__main__', 'new_account': 0, '__init__': , 'work': , '_NewFamily__expenditure': , '__doc__': None}
    {'_NewFamily__expenditure', 'new_account'}
    -------------------- dir(nf) 与 dir(f) 对比 --------------------
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fun', 'name', 'native_place', 'role', 'salary', 'share_money', 'work']
    ['_NewFamily__expenditure', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fun', 'kind', 'name', 'native_place', 'new_account', 'role', 'salary', 'share_money', 'work']
    {'_NewFamily__expenditure', 'new_account', 'kind'}
    -------------------- dir(NewFamily) 与 dir(Family) 对比 --------------------
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fun', 'native_place', 'share_money', 'work']
    ['_NewFamily__expenditure', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'fun', 'native_place', 'new_account', 'share_money', 'work']
    {'_NewFamily__expenditure', 'new_account'}
    
    """
    复制代码

     

  • 相关阅读:
    HTTPS加密协议详解:TLS/SSL握手过程
    使用AlphaFold2进行蛋白质结构预测
    4.9-算法 4.10-伪代码 4.11复杂度 4.12排序
    什么是SpringCloud Eureka服务注册与发现
    超标量处理器设计 姚永斌 第5章 指令集体系 摘录
    Redis主从复制集群
    python 的 import 机制
    Java简系 - Java入门「一」
    Windows OpenGL 图像伽马线
    Supershell防溯源反制配置
  • 原文地址:https://www.cnblogs.com/nbk-zyc/p/11580879.html