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__用于魔术方法,进行了一个简单的介绍,魔术方法较多,但是理解并不复杂。希望以后可以进一步介绍这些魔术方法

以上就是python 下划线的多种应用场景总结的详细内容,更多关于python 下划线应用场景的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python中下划线的使用方法

    本文将讨论Python中下划线(_)字符的使用方法.我们将会看到,正如Python中的很多事情,下划线的不同用法大多数(并非所有)只是常用惯例而已. 单下划线(_) 通常情况下,会在以下3种场景中使用: 1.在解释器中:在这种情况下,"_"代表交互式解释器会话中上一条执行的语句的结果.这种用法首先被标准CPython解释器采用,然后其他类型的解释器也先后采用. >>> _ Traceback (most recent call last): File "&l

  • Python中使用双下划线防止类属性被覆盖问题

    在使用Python编写面向对象的代码时,我们会常常使用"继承"这种开发方式.例如下面这一段代码: class Info: def __init__(self): pass def calc_age(self): print('我是父类的方法') class PeopleInfo(Info): def __init__(self): super().__init__() def calc_age(self): print(123456) 如果你使用 PeopleInfo 初始化一个对象,

  • Python中的下划线详解

    这篇文章讨论Python中下划线_的使用.跟Python中很多用法类似,下划线_的不同用法绝大部分(不全是)都是一种惯例约定. 一. 单个下划线直接做变量名(_) 主要有三种情况: 1. 解释器中 _符号是指交互解释器中最后一次执行语句的返回结果.这种用法最初出现在CPython解释器中,其他解释器后来也都跟进了. 复制代码 代码如下: >>> _ Traceback (most recent call last):   File "", line 1, in Nam

  • Python3中_(下划线)和__(双下划线)的用途和区别

    在看一些Python开源代码时,经常会看到以下划线或者双下划线开头的方法或者属性,到底它们有什么作用,又有什么样的区别呢?今天我们来总结一下(注:下文中的代码在Python3下测试通过) _ 的含义 在python的类中没有真正的私有属性或方法,没有真正的私有化. 但为了编程的需要,我们常常需要区分私有方法和共有方法以方便管理和调用.那么在Python中如何做呢? 一般Python约定加了下划线 _ 的属性和方法为私有方法或属性,以提示该属性和方法不应在外部调用,也不会被from ModuleA

  • 浅谈python 里面的单下划线与双下划线的区别

    在学习Python的时候,很多人都不理解为什么在方法(method)前面会加好几个下划线,有时甚至两边都会加,比如像 __this__ 这种.在我看到上面的文章之前,我一直以为Python中这些下划线的作用就像Golang中方法/函数的大小写一样,或是一些其他语言中的 private . public 的作用一样,但仔细深究,这不全是Python这样设计的初衷.下面我们具体分析. 主要存在四种命名 1. object # 公用方法 2. __object__ # 内建方法,用户不要这样定义 3.

  • 不知道这5种下划线的含义,你就不算真的会Python!

    什么是 Python? Python 之父 Guido van Rossum 说:Python是一种高级程序语言,其核心设计哲学是代码可读性和语法,能够让程序员用很少的代码来表达自己的想法. 对于我来说,学习 Python 的首要原因是,Python 是一种可以优雅编程的语言.它能够简单自然地写出代码和实现我的想法. 另一个原因是我们可以将 Python 用在很多地方:人工智能.数据科学.Web 开发和机器学习等都可以使用 Python 来开发. 国庆期间后台有小伙伴留言问我:"Python变量

  • python中单下划线_的常见用法总结

    这篇文章给大家介绍python中单下划线_,具体内容如下所示: 前言 我们在阅读源码的时候经常会看到各种单下划线_的使用,所以今天特地做一个总结,而且其实很多(不是所有)关于下划线的使用都是一些约定俗成的惯例,而不是真正对python解释器有影响. 0x1存储上一条语句的执行结果 在python的解释器中,_是上一条语句的执行结果,最早是CPython施行,到现在其他类型的解释器也在使用 0x02作为一个无关紧要的变量 例如下面这个例子: for _ in range(10): print 'h

  • Python下划线5种含义代码实例解析

    五种Python下划线模式速查表: 单前导下划线:_var 当涉及到变量和方法名称时,单个下划线前缀有一个约定俗成的含义. 它是对程序员的一个提示 - 意味着Python社区一致认为它应该是什么意思,但程序的行为不受影响. 下划线前缀的含义是告知其他程序员:以单个下划线开头的变量或方法仅供内部使用. 该约定在PEP 8中有定义. 这不是Python强制规定的. Python不像Java那样在"私有"和"公共"变量之间有很强的区别. 这就像有人提出了一个小小的下划线警

  • python 下划线的不同用法

    在本文中,我们将介绍 Python 中 _ 字符的不同用法.就像 Python 中的许多其他内容一样,我们会看到 "_" 的不同用法主要是惯例问题.这里我们将介绍的几种不同情况: 单下划线(例如 _) 名称前加一个下划线(例如 _total) 名称后加一个下划线(例如 total_) 数字文字中的单个下划线(例如 100_000) 名称前加上双下划线(例如 __total) 名称前后加双下划线(例如 __init__) 一.单下划线(_) 单下划线通常在3种情况下使用: 1.在解析程序

  • python中单下划线(_)和双下划线(__)的特殊用法

    函数使用单下划线_开头 使用单下划线(_)开头的函数_func不能被模块外部以: from module import *形式导入. 但可以用:from module import _func形式单独导入. 类属性和类方法使用单下划线_开头 _开头为保护类型的属性和方法,仅允许类内部和子类访问,类实例无法访问此属性和方法. 类属性和类方法使用双下划线__开头 __开头为私有类型属性和方法,仅允许类内部访问,类实例和派生类均不能访问此属性和方法. 所以双划线比单划线权限更严格. 补充说明 对于__

  • Python中的单下划线和双下划线使用场景详解

    单下划线 单下划线用作变量 最常见的一种使用场景是作为变量占位符,使用场景明显可以减少代码中多余变量的使用.为了方便理解,_可以看作被丢弃的变量名称,这样做可以让阅读你代码的人知道,这是个不会被使用的变量,e.g.. for _, _, filenames in os.walk(targetDir): print(filenames) for _ in range(100): print('PythonPoint') 在交互解释器比如iPython中,_变量指向交互解释器中最后一次执行语句的返回

  • Python中单、双下划线的区别总结

    前言 Python 的代码风格由 PEP 8 描述.这个文档描述了 Python 编程风格的方方面面.在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格.这样就易于阅读,易于在程序员之间交流. 我们大家在学习Python的时候,好像很多人都不理解为什么在方法(method)前面会加好几个下划线,有时甚至两边都会加,比如像__this__这种.在我看到上面的文章之前,我一直以为Python中这些下划线的作用就像Golang中方法/函数的大小写一样,或是一些其他语

随机推荐