鱼C论坛

 找回密码
 立即注册
查看: 3354|回复: 15

[知识点备忘] 第064讲:类和对象(VII)

[复制链接]
发表于 2022-6-17 04:40:03 | 显示全部楼层 |阅读模式
购买主题 已有 25 人购买  本主题需向作者支付 5 鱼币 才能浏览
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-9-4 15:48:00 | 显示全部楼层
class C:
    def __init__(self,x):
        self.__x=x
    def set_x(self,x):
        self.__x=x
    def get_x(self):
        print(self.__x)

        
c=C(250)
c.__x
Traceback (most recent call last):
  File "<pyshell#249>", line 1, in <module>
    c.__x
AttributeError: 'C' object has no attribute '__x'
c.get_x()
250
c.set_x(520)
c.get_x()
520
c.__dict__
{'_C__x': 520}
c._C__x
520
class D:
    def __func(self):
        print("hello fishc.")

        
d=D()
d.__func()
Traceback (most recent call last):
  File "<pyshell#260>", line 1, in <module>
    d.__func()
AttributeError: 'D' object has no attribute '__func'. Did you mean: '_D__func'?
d._D__func()
hello fishc.
c.__y=250
c.__dict__
{'_C__x': 520, '__y': 250}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-4 16:22:23 | 显示全部楼层
class C:
    def __init__(self,x):
        self.x=x

        
c=C(250)
c.x
250
c.__dict__
{'x': 250}
c.y=520
c.__dict__
{'x': 250, 'y': 520}
c.__dict__['z']=666
c.__dict__
{'x': 250, 'y': 520, 'z': 666}
c.z
666
class C:
    __slots__=["x","y"]
    def __init__(self,x):
        self.x=x

        
c=C(250)
c.x
250
c.y=520
c.y
520
c.z=666
Traceback (most recent call last):
  File "<pyshell#287>", line 1, in <module>
    c.z=666
AttributeError: 'C' object has no attribute 'z'
class D:
    __slots__=["x","y"]
    def __init__(self,x,y,z):
        self.x=x
        self.y=y
        self.z=z

        
d=D(3,4,5)
Traceback (most recent call last):
  File "<pyshell#295>", line 1, in <module>
    d=D(3,4,5)
  File "<pyshell#294>", line 6, in __init__
    self.z=z
AttributeError: 'D' object has no attribute 'z'
class E(C):
    pass

e=E(250)
e.x
250
e.y=520
e.y
520
e.z=666
e.__slots__
['x', 'y']
e.__dict__
{'z': 666}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-28 11:18:21 | 显示全部楼层
私有变量是一种保护机制,指通过某种手段,使得对象中的属性或方法无法被外部所访问。Python引入的“name mangling”(名字改编、名称改写或名称修饰)机制可以实现“私有化”,只要在变量名前加上两个连续的下横线即可实现,方法名也与之类似。然而,Python其实是将该变量或方法偷换了名字,以此来掩人耳目;__dict__属性可以揭开真相,我们仍可通过“下横线+类名+私有变量名”或“下横线+类名+私有方法名”的方式访问到该变量或方法,只不过在现实中并不提倡这么做,毕竟这会违背私有化的初衷。事实上,Python给予程序员最大的自由度,并不存在仅限从对象内部访问的私有变量。此外,名字改编仅发生在类实例化对象时,实例化对象后添加的属性并不会被改编。
通常来说,单个下横线开头的变量是仅供内部使用的变量,尽量不要随意访问或修改;单个下横线结尾的变量可用于解决命名冲突(如与Python关键字重名)的问题,这是约定俗成的规则。
由于Python动态添加属性背后的实现原理是字典,以空间换时间,会牺牲大量的内存空间,因此,当我们不需要动态添加属性的功能时,便可以使用__slots__属性来避免利用字典造成的空间浪费。我们只需将必要的属性放入列表赋值给__slots__,这样对象就会划分一个固定大小的空间来存放指定的属性,省去了为__dict__属性划分的字典,有时可以大幅减少内存消耗,不失为一个好方法!继承自父类的__slots__属性是不会在子类中生效的,该子类仍可动态添加属性(拥有__dict__字典),因为Python只关注各个具体的类中定义的__slots__属性。虽然使用__slots__属性会牺牲Python动态语言的灵活性,但利用这一副作用又可以限制类属性的滥用,由此可见:技术永远是中立的,因地制宜,对症下药,方能实现最佳疗效!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2023-1-2 23:06:39 | 显示全部楼层
本帖最后由 Ensoleile 于 2023-1-10 00:36 编辑

私有变量和__slots__()
#私有变量:P147 Name Mangling,
# 定义方法:__变量名,外部访问方法:_类名__变量名

#舍得之道:对象中灵活添加的属性,是用字典存储,字典效率快的背后是占用了大量内存(空间换时间)
class C:
    def __init__(self, x):
        self.x = x

c = C(250)
print(c.__dict__)
#{'x': 250}
c.__dict__['y'] = 520
print(c.__dict__)
#{'x': 250, 'y': 520}

#__slots__:如果已经明确一个类的对象设计出来不会有动态添加属性的需求,可以使用__slots__的类属性避免利用字典存放造成空间浪费
class D:
    __slots__ = ['x', 'y'] #列表中的就是希望的属性名
    def __init__(self, x):
        self.x = x

d = D(10)
d.y = 20
print(d.x, d.y, sep='   ')
try:
    print(d.__dict__)
except AttributeError as e:
    print(e)
#'D' object has no attribute '__dict__'
try:
    d.z = 66
except AttributeError as e:
    print(e)
#'D' object has no attribute 'z'

#类的内部同样不允许创建__slots__不包含的属性
class D:
    __slots__ = ['x', 'y']
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
# d = D(3, 4, 5)
#AttributeError: 'D' object has no attribute 'z'
#使用__slots__后,对象就会划分一个固定大小的空间来存放指定属性
#__slots__限制动态添加属性,也可以用来防止属性的滥用

#继承在父类的__slots__属性不会在子类中生效
class E(C):
    pass

e = E(3)
e.z = 10
print(e.__dict__)
#{'x': 3, 'z': 10}
print(e.__slots__)
#AttributeError: 'E' object has no attribute '__slots__'
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2023-1-13 11:43:24 | 显示全部楼层
Ensoleile 发表于 2023-1-2 23:06
私有变量和__slots__()

感谢,以后我也要边学边记笔记然后发评论区

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-2-1 11:37:44 | 显示全部楼层
子类继承__solts__的属性,不会对新加属性造成影响
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-2-7 15:16:14 | 显示全部楼层
class C:
    def __init__(self,x):
        self.__x = x
    def set_x(self,x):
        self.__x = x
    def get_x(self):
        print(self.__x)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-2-17 17:48:22 | 显示全部楼层
get私有变量
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-2-21 16:55:37 | 显示全部楼层
下节课没知识备忘了,也没作业了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-3-30 16:12:05 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-26 23:04:34 | 显示全部楼层
懂了,就像电算时代之前,银行不乐意给个人储户开活期储存,以缩限每个账户(存折)属性量,进而大大减小人工操作和局端存底成本。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-1-22 14:14:42 | 显示全部楼层
在线等,求教:一个函数中如何添加多个私有变量啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-7-19 09:21:00 | 显示全部楼层
leizhenzi23 发表于 2024-1-22 14:14
在线等,求教:一个函数中如何添加多个私有变量啊

请问你是说这样子吗
class A:
    def __init__(self,x,y):
        self.__x=x
        self.__y=y
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-1-2 17:28:23 | 显示全部楼层
>>> class E(C):
...     pass
...
>>> e = E(250)
>>> e.x
250
>>> e.y = 520
>>> e.z = 666
>>> e.__slots__
['x', 'y']

在最后的这个继承中,为什么e.__dict__中只有z,没有x,y呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-1-24 13:28:37 | 显示全部楼层
# 1. 私有变量
# 在大多数面向对象的编程语言中,都存在着私有变量(private variable)的概念,所谓私有变量,就是指通过某种手段,使得对象中的属性或方法无法被外部所访问。
# Python对于私有变量的实现是引入了一种叫name mangling 的机制(翻译过来叫“名字改编”“名称改写”或者“名称修饰”),语法是在变量名前面加上两个连续下划线(__)
class C:
    def __init__(self, x):
        self.__x = x
    def set_x(self, x):
        self.__x = x
    def get_x(self):
        print(self.__x)

c = C(250)
#此时,我们是无法直接通过变量名访问到该变量的:
# c.__x
# Traceback (most recent call last):
#   File "<pyshell#13>", line 1, in <module> c.__x
# AttributeError: 'C' object has no attribute '__x

#想要访问变量的值,就需要使用指定的接口,比如这段代码中的 set_x() 和 get_x() 方法:
c.get_x()
c.set_x(520)
c.get_x()

#2. name mangling 机制的实现原理   我们看看 __dict__ 属性里面有啥:
print(c.__dict__)  #虽然这里面没有看到 __x,但是,却多了一个 _C__x 的属性对不对?
#访问一下试试:
print(c._C__x)   #果然如此……这个就是传说中的名字改编术!

#方法名也是同样的道理:
class D:
    def __func(self):
        print("Hello FishC.")

d = D()
#d.__func()
# Traceback (most recent call last):
#   File "<pyshell#12>", line 1, in <module>
#     d.__func()   AttributeError: 'D' object has no attribute '__func'
d._D__func()
#注意:name mangling 机制是发生在类实例化对象时候的事情,给对象动态添加属性则不会有同样的效果!!!

# 3. 不同数量的前缀下划线含义
# 不同数量的前缀下划线均有不同的特殊含义:

# 单下划线前缀  _FishC  仅供内部使用的变量(约定俗成,无强制措施)
# 双下划线前缀  __FishC  私有变量,Python 启用 name mangling 机制修改变量名称
# 单下划线后缀  class_  通常为了避免与 Python 关键字命名冲突使用,比如想定义一个叫class的变量名,但无奈class是属于 Python 的关键字,所以你可以写成 class_
# 前后均有双下划线  __init__  Python 魔法方法的命名方案,我们应该避免自己的属性使用这种命名形式
# 单独一个下划线  _  表示一个临时变量,在 IDLE 的交互模式中,_ 是最后一个表达式的结果


# 4. 效率提升之道
# Python 对象存储属性的工作原理 —— 字典(__dict__):
# 对象动态添加属性,就是将键值对添加到 __dict__ 中:
# 甚至你可以直接通过给字典添加键值对的形式来创建对象的属性:c.__dict__['z'] = 666

# 但是,字典高效率的背后是以付出更多存储空间为代价的(字典和集合高效背后的玄机)
# 如果我们明确知道一个类的对象设计出来,就只是需要那么固定的某几个属性,并且不需要有动态添加属性这样的功能,那么利用字典来存放属性,这种空间上的牺牲就是纯纯地浪费!
# 针对这个情况,Python 专门设计了一个 __slots__ 类属性,避免了利用字典存放属性造成空间上的浪费
class C:
    __slots__ = ['x', 'y']
    def __init__(self, x):
        self.x = x
c = C(250)
print(c.x)
c.y=666
print(c.y)
#如果想要动态地添加一个属性,那就不好意思了:
# c.z = 100
# Traceback (most recent call last):
#   File "<pyshell#27>", line 1, in <module>
#     c.z = 100
# AttributeError: 'C' object has no attribute 'z'

# 这种限制不仅体现在动态添加属性上,如果在类的内部,想创建一个 __slots__ 不包含的属性,也是不被允许的
class D:
    __slots__ = ['x', 'y']
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

# d = D(3, 4, 5)
#   File "<pyshell#6>", line 6, in __init__
#     self.z = z
# AttributeError: 'D' object has no attribute 'z'


# 甚至是 __dict__ 属性,也不存在了:
#  d.__dict__
# AttributeError: 'D' object has no attribute '__dict__'


#因为使用了 __slots__ 属性,那么对象就会划分一个固定大小的空间来存放指定的属性,这时候 __dict__ 属性就不需要了,空间也就节约了出来。
# 不过这里有一点是需要特别强调的,就是使用 __slots__ 属性的副作用其实也相当明显,那就是要以牺牲 Python 动态语言的灵活性,作为前提。
# 使用了 __slots__ 属性,就没办法再拥有动态添加属性的功能了……
# 这可以说是它的一个副作用,但实际上很多开发者却利用这个副作用,来限制类属性的滥用4

#最后,还有一点需要大家知道的是,继承自父类的 __slots__ 属性是不会在子类中生效的,Python 只关注各个具体的类中定义的 __slots__ 属性:
class E(C):
    pass

e = E(250)
e.x

e.y = 520
e.z = 666
print(e.__slots__)   #对象 e 虽然拥有 __slots__ 属性,但它同时也拥有 __dict__ 属性:
print(e.__dict__)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-3-31 15:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表