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

时间:2022-10-04 22:08:55

1. 面向对象的概念

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

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

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

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

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

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

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

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

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

python中类与对象的命名空间(静态属性的陷阱)、__dict__ 和 dir() 在继承中使用说明
 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
python中类与对象的命名空间(静态属性的陷阱)、__dict__ 和 dir() 在继承中使用说明
 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'
27 # Rectangle.temVar = 'class_over'

总结:

# 对象 = 类名()

# 实例化的过程:

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

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

    # 执行__init__(self)方法

    # 返回self

# 对象能做的事:

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

  # 调用方法

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

# 类名能做的事:

   # 实例化

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

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

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

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

(1)类的命名空间

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

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

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

(2)对象的命名空间

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

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

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

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

 完整代码展示:

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__': <function Family.__init__ at 0x0000021C360084C8>, 
'work': <function Family.work at 0x0000021C3629C048>, '__dict__': <attribute '__dict__' of 'Family' objects>, 
'__weakref__': <attribute '__weakref__' of 'Family' objects>}
"""

"""
可变数据类型做静态属性的影响:
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__': <function Family.__init__ at 0x000002E4747684C8>, 
'work': <function Family.work at 0x000002E4749FC048>, '__dict__': <attribute '__dict__' of 'Family' objects>, 
'__weakref__': <attribute '__weakref__' of 'Family' objects>}
"""

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

(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__': <function Family.__init__ at 0x000001EC8159D288>, 'work': <function Family.work at 0x000001EC8159D318>, 'fun': <function Family.fun at 0x000001EC8159D3A8>, '__dict__': <attribute '__dict__' of 'Family' objects>, '__weakref__': <attribute '__weakref__' of 'Family' objects>}
{'__module__': '__main__', 'new_account': 0, '__init__': <function NewFamily.__init__ at 0x000001EC8159D438>, 'work': <function NewFamily.work at 0x000001EC8159D4C8>, '_NewFamily__expenditure': <function NewFamily.__expenditure at 0x000001EC8159D558>, '__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'}

"""