乐趣区

关于python:Python-里最具代表性的符号竟如此强大

Python 有很多中央应用下划线。在不同场合下,有不同含意:比方_var 示意外部变量;__var 示意公有属性;__var__示意魔术办法;这些含意有的是程序员群体的约定,如_var;有的是 Python 解释器规定的模式,如__var。

本文总结 Python 语言编程中罕用下划线的中央,力求一次搞懂_用法。目前常见的用法有五种:

  • _用于长期变量
  • var_用于解决命名抵触问题
  • _var 用于爱护变量
  • __var 用于公有变量
  • __var__用于魔术办法

上面咱们具体看看这些下划线利用场景。

一、_用于长期变量
单下划线个别用于示意长期变量,在 REPL、for 循环和元组拆包等场景中比拟常见。

1.1 REPL
单下划线在 REPL 中关联的是上一次计算的非 None 后果。

>>> 1+1
2
>>> _
2
>>> a=2+2
>>> _
2

1+1,后果为 2,赋值给_;而赋值表达式 a =2+2a 为 4,但整个表达式后果为 None,故不会关联到_。这有点相似日常大家应用的计算器中的 ANS 按键,间接保留了上次的计算结果。

1.2 for 循环中的_
for 循环中_作为长期变量用。下划线来指代没什么意义的变量。例如在如下函数中,当咱们只关怀函数执行次数,而不关怀具体秩序的状况下,能够应用_作为参数。

nums = 13
for _ in range(nums):
    fun_oper()

1.3 元组拆包中的_
第三个用法是元组拆包,赋值的时候能够用_来示意略过的内容。如下代码疏忽北京市人口数,只获得名字和区号。

>>> city,_,code = ('Beijing',21536000,'010')
>>> print(city,code)
Beijing 010

如果须要略过的内容多于一个的话,能够应用 * 结尾的参数,示意疏忽多个内容。如下代码疏忽面积和人口数,只获得名字和区号

city,*_,code = ('Beijing',21536000,16410.54,'010')

1.4 国际化函数
在一些国际化编程中,_罕用来示意翻译函数名。例如 gettext 包应用时:

import gettext
zh = gettext.tranlation('dict','locale',languages=['zh_CN'])
zh.install()
_('hello world')

根据设定的字典文件,其返回相应的汉字“你好世界”。

1.5 大数字示意模式
_也可用于数字的宰割,这在数字比拟长的时候罕用。

>>> a = 9_999_999_999
>>> a
9999999999

a 的值主动疏忽了下划线。这样用_宰割数字,有利于便捷读取比拟大的数。

二、var_用于解决命名抵触问题
变量前面加一个下划线。次要用于解决命名抵触问题,元编程中遇时 Python 保留的关键字时,须要长期创立一个变量的正本时,都能够应用这种机制。

def type_obj_class(name,class_):
    pass

def tag(name,*content,class_):
    pass

以上代码中呈现的 class 是 Python 的保留关键字,间接应用会报错,应用下划线后缀的形式解决了这个问题。

三、_var 用于爱护变量
后面一个下划线,前面加上变量,这是仅供外部应用的“爱护变量”。比方函数、办法或者属性。

这种爱护不是强制规定,而是一种程序员的约定,解释器不做访问控制。一般来讲这些属性都作为实现细节而不须要调用者关怀,随时都可能扭转,咱们编程时尽管能拜访,然而不倡议拜访。

这种属性,只有在导入时,能力施展爱护作用。而且必须是 from XXX import * 这种导入模式能力施展爱护作用。

应用 from XXX import * 是一种通配导入(wildcard import),这是 Python 社区不举荐的形式,因为你基本搞不清你到底导入了什么属性、办法,很可能搞乱你本人的命名空间。PEP8 举荐的导入形式是 from XXX import aVar , b_func , c_func 这种模式。

比方在下例汽车库函数 tools.py 里定义的“爱护属性”:发动机型号和轮胎型号,这属于实现细节,没必要裸露给用户。当咱们应用 from tools import * 语句调用时,其理论并没有导入所有_结尾的属性,只导入了一般 drive 办法。


_moto_type = 'L15b2'
_wheel_type = 'michelin'

def drive():
    _start_engine()
    _drive_wheel()

def _start_engine():
    print('start engine %s'%_moto_type)
    
def _drive_wheel():
    print('drive wheel %s'%_wheel_type)

查看命令空间 print(vars())可见,只有 drive 函数被导入进来,其余下划线结尾的“公有属性”都没有导入进来。

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x005CF868>, '__spec__': None, '__annotations__':{}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '.\\xiahuaxian.py', '__cached__': None, 'walk': <function walk at 0x01DA8C40>, 'root': '.\\__pycache__', '_': [21536000, 16410.54], 'dirs': ['tools.cpython-38.pyc'], 'city': 'Beijing', 'code': '010', 'drive': <function drive at 0x01DBC4A8>}

3.1 冲破爱护属性
之所以说是“爱护”并不是“公有”,是因为 Python 没有提供解释器机制来管制拜访权限。咱们仍然能够拜访这些属性:

import tools
tools._moto_type = 'EA211'
tools.drive()

以上代码,以越过“爱护属性”。此外,还有两种办法能冲破这个限度,一种是将“公有属性”增加到 tool.py 文件的__all__列表里,使 from tools import * 也导入这些本该暗藏的属性。

__all__ = ['drive','_moto_type','_wheel_type']

另一种是导入时指定“受爱护属性”名。

from tools import drive,_start_engine
_start_engine()

甚至是,应用 import tools 也能够轻易冲破爱护限度。所以可见,“爱护属性”是一种简略的暗藏机制,只有在 from tools import * 时,由解释器提供简略的爱护,然而能够轻易冲破。这种爱护更多地依赖程序员的共识:不拜访、批改“爱护属性”。除此之外,有没有更平安的爱护机制呢?有,就是下一部分探讨的公有变量。

四、__var 用于公有变量

公有属性解决的之前的爱护属性爱护力度不够的问题。变量后面加上两个下划线,类外面作为属性名和办法都能够。两个下划线属性由 Python 的改写机制来实现对这个属性的爱护。

看上面汽车例子中,品牌为一般属性,发动机为“爱护属性”,车轮品牌为“公有属性”。

class Car:
    def __init__(self):
        self.brand = 'Honda'
        self._moto_type = 'L15B2'
        self.__wheel_type = 'michelin'

    def drive(self):
        print('Start the engine %s,drive the wheel %s,I get a running %s car'%
        (self._moto_type,
        self.__wheel_type,
        self.brand))

咱们用 var(car1)查看下具体属性值,

['_Car__wheel_type', '__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__', '_moto_type', 'brand', 'drive']

可见,实例化 car1 中,一般属性 self.brand 和爱护属性 self._moto_type 都得以保留,两个下划线的公有属性__wheel_type 没有了。取而代之的是_Car_wheel_type 这个属性。这就是改写机制(Name mangling)。两个下划线的属性,被改写成带有类名前缀的变量,这样子类很难明明一个和如此简单名字重名的属性。保障了属性不被重载,保障了其的公有性。

4.1 冲破公有属性
这里“公有变量”的实现,是从解释器层面给与的改写,爱护了公有变量。然而这个机制并非相对平安,因为咱们仍然能够通过 obj._ClasssName__private 来拜访__private 公有属性。

car1.brand = 'Toyota'
car1._moto_type = '6AR-FSE'
car1._Car__wheel_type = 'BRIDGESTONE'
car1.drive()

后果

Start the engine 6AR-FSE,\
drive the wheel BRIDGESTONE,\
I get a running Toyota car

可见,对改写机制改写的公有变量,尽管保护性增强了,但仍然能够拜访并批改。只是这种批改,只是一种杂耍般的操作,并不可取。

五、__var__用于魔术办法

变量后面两个下划线,前面两个下划线。这是 Python 当中的魔术办法,个别是给零碎程序调用的。例如上例中的__init__就是类的初始化魔术办法,还有反对 len 函数的__len__办法,反对上下文管理器协定的__enter__和__exit__办法,反对迭代器协定的__iter__办法,反对格式化显示的__repr__和__str__办法等等。这里咱们为上例的 Car 类增加魔术办法__repr__来反对格式化显示。

    def __repr__(self):
        return '***Car %s:with %s Engine,%sWheel***'%
        (self.brand,self._moto_type,self.__wheel_type)

未增加__repr__魔术办法之前,print(car1)后果为 <__main__.Car object at 0x0047F7F0>,这个后果让人看的一头雾水,减少 repr 魔术办法之后,显示后果为 Car Toyota:with 6AR-FSE Engine,BRIDGESTONE Wheel 清晰明了,利于调试。这就是魔术办法的效用:支持系统调用,改良用户类体现,减少协定反对,使用户类体现得更像零碎类。

5.1 Python 魔术办法分类
以下所有魔术办法均须要在前后加上__,这里省略了这些双下划线。

一元运算符 neg pos abs invert
转换 complex int float round inex
算术运算 add sub mul truediv floordiv mod divmod pow lshift rshift and xor or

算术运算除 and 之外,后面再加上 r,示意反运算。除 dimod 外,后面加上 i,示意就地运算。

  • 比拟 lt le eq ne gt ge
  • 类属性 getattr getattribute setattr delattr dir get set delete
  • 格式化 bytes hash bool format
  • 类相干 init del new
  • 列表 getitem
  • 迭代器 iter next
  • 上下文管理器 enter exit

六、总结
总之,下划线在 Python 当中利用还是很宽泛的,甚至能够说 Python 对下划线有所偏爱

能够看到 _罕用于长期变量,在 REPL,for 循环,元组拆包和国际化中失去了广泛应用

var_用于解决命名抵触问题,应用时比较简单易懂的。_var 对变量的爱护,只是一种软弱的爱护,更多依附程序员的约定。__var 用于公有变量,借助改写机制反对,曾经反对了公有变量,然而依然存在破绽

对__var__用于魔术办法,进行了一个简略的介绍,魔术办法较多,然而了解并不简单。心愿当前能够进一步介绍这些魔术办法

最近整顿了几百 G 的 Python 学习材料,蕴含新手入门电子书、教程、源码等等,收费分享给大家!想要的返回“Python 编程学习圈”,发送“J”即可收费取得

退出移动版