python中的range函数join()函数的参数有哪些呢?

内容概要:如何把列表中的元素拼接为一个字符串呢?本文介绍了采用 join() 函数的解决方法。

问题:有一个列表,比如:letters=[‘a’,‘b’,‘c’],想要把列表的元素连续显示出来,应该怎么办?

如何使Python中的print()语句运行结果不换行 的方法,设置 end=’’。

把列表中的元素拼接为一个字符串(string),然后显示字符串。

这里用到了一个神奇的函数:join() ,它可以把列表拼接转换成一个字符串,用法为:字符串=‘分隔符’.join(列表)。

另一种情况也适合用 join() 函数:比如有一个词汇构成的列表,需要把这几个词汇组成一个短语。可以把分隔符设置为空格符,用' '.join()实现组合。

不仅如此,我们还可以在 join() 函数内调用其它函数,如大小写转换、类型转换等,从而一次实现多个功能。这就要通过在 join() 函数中使用 list comprehension(列表推导式)来实现,即:join(function(e) for e in list)

(1)比如,列表中的字符既有大写,又有小写。想在拼接的同时,把字符都转换为大写,应该怎么做呢?

方法:在 join() 函数中加入大小写转换的函数 upper()。

(2)前面提到,join() 函数是把列表的元素拼接为字符串。因此,列表中的元素需要是 string(字符串)类型。如果是一个数字列表,可以使用 join() 函数吗?

可以。只要在join() 函数中加入类型转换,将数字转换为 string 型即可。

(3)除了用 Python 的函数,我们还可以应用自定义的函数。

比如,下面的代码先定义了一个convert() 函数,如果字母是 a 或 A,那么保持不变;其它字母则转换为小写。然后,在 join() 函数中应用 convert() 函数。

到此这篇关于如何在Python 中使用 join() 函数把列表拼接成一个字符串的文章就介绍到这了,更多相关 join() 把列表拼成字符串内容请搜索AB教程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持AB教程网!

内容概要:如何把列表中的元素拼接为一个字符串呢?本文介绍了采用 join() 函数的解决方法。

问题:有一个列表,比如:letters=[‘a’,‘b’,‘c’],想要把列表的元素连续显示出来,应该怎么办?

如何使Python中的print()语句运行结果不换行 的方法,设置 end=’’。

把列表中的元素拼接为一个字符串(string),然后显示字符串。

这里用到了一个神奇的函数:join() ,它可以把列表拼接转换成一个字符串,用法为:字符串=‘分隔符’.join(列表)。

另一种情况也适合用 join() 函数:比如有一个词汇构成的列表,需要把这几个词汇组成一个短语。可以把分隔符设置为空格符,用' '.join()实现组合。

不仅如此,我们还可以在 join() 函数内调用其它函数,如大小写转换、类型转换等,从而一次实现多个功能。这就要通过在 join() 函数中使用 list comprehension(列表推导式)来实现,即:join(function(e) for e in list)

(1)比如,列表中的字符既有大写,又有小写。想在拼接的同时,把字符都转换为大写,应该怎么做呢?

方法:在 join() 函数中加入大小写转换的函数 upper()。

(2)前面提到,join() 函数是把列表的元素拼接为字符串。因此,列表中的元素需要是 string(字符串)类型。如果是一个数字列表,可以使用 join() 函数吗?

可以。只要在join() 函数中加入类型转换,将数字转换为 string 型即可。

(3)除了用 Python 的函数,我们还可以应用自定义的函数。

比如,下面的代码先定义了一个convert() 函数,如果字母是 a 或 A,那么保持不变;其它字母则转换为小写。然后,在 join() 函数中应用 convert() 函数。

到此这篇关于如何在Python 中使用 join() 函数把列表拼接成一个字符串的文章就介绍到这了,更多相关 join() 把列表拼成字符串内容请搜索AB教程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持AB教程网!

如果您问Python程序员他们最喜欢Python的什么,他们通常会说它的可读性很高。事实上,高水平的可读性是Python语言设计的核心,遵循这样一个公认的事实,即读取代码的次数比编写代码的次数多得多。

Python代码可读性高的一个原因是它相对完整的一套代码风格准则和“pythonic”习语。

当一个资深的python开发人员(pythonista)调用代码的一部分而不是“pythonic”时,通常意味着这些代码行不遵循通用的指导原则,并且无法以最佳(hear:most readable)方式表达其意图。

在一些边界案例中,关于如何在Python代码中表达意图的最佳方法尚未达成一致,但这些案例很少。

虽然任何一种黑魔法都可以通过Python实现,但最好是最明确和直接的方式。

在上面的好代码中,x和y是从调用方显式接收的,并返回显式字典。使用此函数的开发人员通过读取第一行和最后一行确切地知道要做什么,而坏例子并非如此。

虽然有些复合语句(如列表理解)因其简洁性和表达性而被允许和欣赏,但在同一行代码上有两个不连贯的语句是不好的做法。

参数可以通过四种不同的方式传递给函数。

  1. 位置参数 是必需的,没有默认值。它们是参数的最简单形式,可以用于几个函数参数,这些参数完全是函数意义的一部分,它们的顺序是自然的。例如,在 send(message, recipient)point(x, y) 函数的用户不难记住,这两个函数需要两个参数,并且按照哪个顺序。

  1. 关键字参数 不是必需的,并且具有默认值。它们通常用于发送给函数的可选参数。当一个函数有两个或三个以上的位置参数时,它的签名更难记住,并且使用带有默认值的关键字参数很有帮助。例如,一个更完整的 send 函数可以定义为 send(message, 当它们没有被传递给另一个值时。

在Python中,可以通过多种方式调用带有关键字参数的函数;例如,可以按照定义中参数的顺序进行调用,而无需显式命名参数,如 send('Hello', 'World', 'Cthulhu', 'God')

作为旁注,遵循 原则上,删除添加了“以防万一”且似乎从未使用过的可选参数(及其函数内部的逻辑)通常比在需要时添加新的可选参数及其逻辑更困难。

  1. 这个 任意参数列表 是向函数传递参数的第三种方法。如果函数意图更好地由具有可扩展数量的位置参数的签名表示,则可以使用 *args 构造。在功能体中, args 将是所有剩余位置参数的元组。例如,

然而,这个构造有一些缺点,应该谨慎使用。如果一个函数接收到一个具有相同性质的参数列表,那么通常更清楚地定义它为一个参数的函数,该参数是一个列表或任何序列。这里,如果 send 有多个收件人,最好明确定义: send(message, .这样,函数的用户可以预先将收件人列表作为一个列表来操作,并且可以传递任何不能作为其他序列解包的序列,包括迭代器。

  1. 这个 任意关键字参数字典 是向函数传递参数的最后一种方法。如果函数需要一系列未确定的命名参数,则可以使用 **kwargs 构造。在功能体中, kwargs 将是所有传递的命名参数的字典,这些参数未被函数签名中的其他关键字参数捕获。

注意事项与 任意参数列表 是必要的,出于类似的原因:当有必要使用这些强大的技术时,可以使用这些技术;如果更简单、更清晰的结构足以表达功能的意图,则不应使用这些技术。

由程序员编写函数来决定哪些参数是位置参数,哪些是可选关键字参数,以及决定是否使用高级的任意参数传递技术。如果明智地遵循上面的建议,那么编写以下python函数是可能的,也是令人愉快的:

  • 易于阅读(名称和参数无需解释)

  • 易于更改(添加新的关键字参数不会破坏代码的其他部分)

对于黑客来说,python是一个强大的工具,它有一套非常丰富的钩子和工具,可以让你做几乎任何一种棘手的技巧。例如,可以执行以下每项操作:

  • 更改对象的创建和实例化方式

  • 更改python解释器导入模块的方式

  • 甚至可以(如果需要,建议)在Python中嵌入C例程。

然而,所有这些选项都有许多缺点,最好使用最直接的方式来实现您的目标。主要的缺点是在使用这些结构时,可读性会受到很大的影响。许多代码分析工具,如pylint或pyflakes,将无法解析这个“魔力”代码。

我们认为,Python开发人员应该了解这些几乎无限的可能性,因为它会给人们灌输信心,即不会有任何无法解决的问题。然而,知道如何,尤其是何时 not 使用它们是非常重要的。

像功夫大师一样, Python 知道如何用一根手指杀人,而且从来不会真的这么做。

如上所述,python允许使用许多技巧,其中一些技巧可能很危险。一个很好的例子是,任何客户机代码都可以覆盖对象的属性和方法:Python中没有“private”关键字。这种哲学,与Java这样的高度防御语言非常不同,它提供了很多防止任何误用的机制,用“我们都是负责任的用户”这一说法来表达。

这并不意味着,例如,没有属性被认为是私有的,在Python中也不可能进行适当的封装。相反,Python社区更倾向于依赖一组约定,指示不应该直接访问这些元素,而不是依赖开发人员在代码和其他代码之间建立的混凝土墙。

私有属性和实现细节的主要约定是在所有“内部”前面加下划线。如果客户机代码违反了此规则并访问了这些标记的元素,那么在修改代码时遇到的任何错误行为或问题都是客户机代码的责任。

鼓励慷慨地使用此约定:任何不打算由客户机代码使用的方法或属性都应该以下划线作为前缀。这将保证更好的职责分离和更容易修改现有的代码;公开私有财产总是可能的,但是将公共财产私有化可能是一个更困难的操作。

当函数变得越来越复杂时,在函数体中使用多个返回语句并不少见。但是,为了保持清晰的意图和可持续的可读性水平,最好避免从正文中的许多输出点返回有意义的值。

函数中返回值的主要情况有两种:函数正常处理时返回的结果,以及指示错误输入参数或函数无法完成其计算或任务的任何其他原因的错误情况。

如果不希望引发第二种情况的异常,则返回一个值,例如none或false,表示可能需要函数无法正确执行。在这种情况下,最好在检测到不正确的上下文时尽早返回。它将有助于扁平化函数的结构:返回后的所有代码都可以假定满足条件以进一步计算函数的主要结果。通常需要有多个这样的返回语句。

但是,当一个函数的正常过程中有多个主出口点时,调试返回的结果变得困难,因此最好保留一个出口点。这也将有助于分解出一些代码路径,并且多个出口点可能表示需要这样的重构。

简单地说,编程成语是 way 编写代码。编程习语的概念在 在 .

尽管通常有一种——最好只有一种——显而易见的方法来做到这一点; the 编写惯用的python代码的方法对于python初学者来说是不明显的。所以,好的习语必须有意识地习得。

一些常见的python习惯用法如下:

如果知道列表或元组的长度,可以通过解包为其元素指定名称。例如,因为 enumerate() 将为列表中的每个项提供两个元素的元组:

您也可以使用它交换变量:

在Python 3中,引入了一种新的扩展解包方法 :

如果您需要分配某些内容(例如, )但不需要那个变量,使用 __

许多Python风格的指南建议对一次性变量使用一个下划线“`”,而不是这里推荐的双下划线“``”。问题是,“通常用作 函数,并在交互提示下用于保存上一个操作的值。相反,使用双下划线也同样清晰和方便,并且消除了意外干扰这些其他用例的风险。

创建一个相同事物的length-n列表

创建一个长度为n的列表列表

因为列表是可变的, * 操作员(如上所述)将创建一个对 same 列表,这不太可能是您想要的。相反,使用列表理解:

创建字符串的常见习惯用法是 在空字符串上。

这将设置变量的值 word “垃圾邮件”。这个习语可以应用于列表和元组。

也称为 ,Python设计的指导原则。

有关良好的Python样式的一些示例,请参见 。

实际上是Python的代码样式指南。PEP 8的高质量、易读版本也可在 。

强烈建议阅读。整个Python社区都尽最大努力遵守本文档中列出的指导原则。有些项目可能会不时地受到影响,而另一些项目可能会 。

也就是说,让您的Python代码符合PEP8通常是一个好主意,有助于使代码在与其他开发人员一起处理项目时更加一致。有一个命令行程序, (以前称为 pep8 ,这可以检查代码的一致性。通过在终端中运行以下命令进行安装:

然后在一个文件或一系列文件上运行它,以获取任何违规的报告。

有几个自动格式化工具可以重新格式化您的代码,以符合PEP8。

程序 可用于自动重新格式化PEP 8样式的代码。安装程序时使用:

使用它来设置文件的格式:

不包括 --in-place 标志将使程序直接将修改后的代码输出到控制台以供查看。这个 --aggressive 标志将执行更多实质性的更改,并且可以多次应用以获得更大的效果。

虽然autopep8专注于解决PEP8违规问题, 除了遵循PEP8之外,它还试图改进代码的格式。这个格式化程序的目标是提供与编写PEP8兼容代码的程序员一样美观的代码。它与以下组件一起安装:

使用以下命令运行文件的自动格式化:

与autopep8类似,运行该命令时不使用 --in-place 在应用更改之前,FLAG将输出差异以供检查。

自动格式化程序 提供对代码库的固执己见和确定性的重新格式化。它的主要关注点在于提供统一的代码样式,而不需要对所有用户进行配置。因此,黑色的用户可以完全忘记格式化。此外,由于采用了确定性方法,保证了仅有相关更改的最小git差异。您可以按如下方式安装该工具:

Python文件可以使用以下格式进行格式化:

添加 --diff FLAG提供代码修改以供审查,不直接应用。

下面是一些您应该遵循的惯例,以使您的代码更容易阅读。

您不需要显式地将值与true、none或0进行比较——只需将其添加到if语句中即可。参见 一个被认为是错误的列表。

提供了一种强大、简洁的列表处理方式。

遵循与列表理解几乎相同的语法,但返回生成器而不是列表。

创建新列表需要更多的工作和内存。如果您只想循环访问新列表,那么最好使用迭代器。

当您确实需要创建第二个列表时,例如,如果您需要多次使用结果,请使用列表理解。

如果您的逻辑对于简短的列表理解或生成器表达式来说过于复杂,请考虑使用生成器函数而不是返回列表。

不要仅仅为了副作用而使用清单理解。

在迭代列表时,不要从列表中删除项目。

不要在列表中进行多次传递。

使用列表理解或生成器表达式。

记住,赋值永远不会创建新对象。如果两个或多个变量引用同一个列表,则更改其中一个变量将更改所有变量。

创建一个新的列表对象并让原始对象单独存在更安全。

使用 数一数你在名单上的位置。

这个 函数具有比手动处理计数器更好的可读性。此外,它对迭代器进行了更好的优化。

使用 with open 从文件中读取的语法。这将自动为您关闭文件。

with 语句更好,因为它可以确保您始终关闭文件,即使 with 块中引发了异常。

当逻辑代码行长于接受的限制时,需要将其拆分为多个物理行。如果行的最后一个字符是反斜杠,python解释器将连接连续的行。这在某些情况下是有帮助的,但通常应该避免,因为它的脆弱性:在行的末尾加上一个空格,在反斜杠之后,将会破坏代码,并可能产生意想不到的结果。

更好的解决方案是在元素两边使用括号。在行尾留有一个未结束的圆括号,Python解释器将连接下一行,直到圆括号结束。同样的行为也适用于花括号和方括号。

但是,通常情况下,必须拆分一个长逻辑行是一个信号,表明您试图同时执行过多操作,这可能会妨碍可读性。

以下介绍了一些 Python 的调试器,内置函数 允许你使用其中的任何一种。

pdb 模块是一个简单但是够用的控制台模式 Python 调试器。 它是标准 Python 库的一部分,并且 。 你也可以通过使用 pdb 代码作为样例来编写你自己的调试器。

作为标准 Python 发行版附带组件的 IDLE 交互式环境(通常位于 Tools/scripts/idle)中包含一个图形化的调试器。

是一个类似 gdb 的调试器。

是一个带有集成了版本控制软件的调试工具的 IDE。

有许多商业Python IDE包括图形调试器。他们包括:

和 可进行基本检查来帮助你尽早捕捉漏洞。

静态类型检查器,例如 、 和 可以检查Python源代码中的类型提示。

如果你想要的只是一个独立的程序,用户可以下载和运行而不必先安装Python发行版,你就不需要将Python编译成C代码。有许多工具可以确定程序所需的模块集,并将这些模块与Python二进制文件绑定在一起以生成单个可执行文件。

一种是使用冻结工具,它包含在Python源代码树 Tools/freeze 中。它将Python字节代码转换为C数组;一个C编译器,你可以将所有模块嵌入到一个新程序中,然后将其与标准Python模块链接。

它的工作原理是递归扫描源代码以获取import语句(两种形式),并在标准Python路径和源目录(用于内置模块)中查找模块。 然后,它将用Python编写的模块的字节码转换为C代码(可以使用编组模块转换为代码对象的数组初始化器),并创建一个定制的配置文件,该文件仅包含程序中实际使用的内置模块。 然后,它编译生成的C代码并将其与Python解释器的其余部分链接,以形成一个独立的二进制文件,其行为与你的脚本完全相同。

显然,freeze 需要一个 C 编译器。 还有一些其他实用工具则不需要:

  • 用于生成 Windows 版二进制可执行文件

  • 用于生成 Mac OS X 版二进制可执行文件

  • 用于生成跨平台的二进制可执行文件

有的。 请参阅标准库模块所要求的代码风格描述文档 。

通过在函数体中的某处添加赋值语句,导致以前正常工作的代码被修改而得到 UnboundLocalError 会令人感到意外。

正常工作,但是以下代码

这是因为当你对作用域中的变量进行赋值时,该变量将成为该作用域的局部变量,并在外部作用域中隐藏任何类似命名的变量。由于foo中的最后一个语句为 x 分配了一个新值,编译器会将其识别为局部变量。因此,当先前的 print(x) 尝试打印未初始化的局部变量时会导致错误。

在上面的示例中,你可以通过将其声明为全局来访问外部作用域变量:

这个显式声明是必需的,以便提醒你(与类和实例变量的表面类似情况不同),你实际上是在外部作用域中修改变量的值

你可以使用 关键字在嵌套作用域中执行类似的操作:

在Python中,仅在函数内引用的变量是隐式全局变量。如果在函数体内的任何位置为变量赋值,则除非明确声明为全局,否则将其视为局部值。

虽然起初有点令人惊讶,但片刻考虑就可以解释。一方面,要求 表示已分配的变量可以防止意外的副作用。另一方面,如果所有全局引用都需要 global ,那么你一直都在使用 global 。你必须将对内置函数或导入模块的组件的每个引用声明为全局。这种杂乱会破坏 global 声明用于识别副作用的有用性。

假设你使用for循环来定义几个不同的 lambda (甚至是普通函数),例如::

这给你一个包含5个lambdas的列表,它们计算 x**2 。你可能会期望,当它们被调用时,它们将分别返回 014916 。但是,当你真正尝试时,你会看到它们都返回 16 。:

发生这种情况是因为 x 不是lambdas的内部变量,而是在外部作用域中定义,并且在调用lambda时访问它 - 而不是在定义它时。 在循环结束时, x 的值是 4 ,所以所有的函数现在返回 4**2 ,即 16 。你还可以通过更改 x 的值来验证这一点,并查看lambdas的结果如何变化:

为了避免这种情况,你需要将值保存在lambdas的局部变量中,这样它们就不依赖于全局``x`` 的值

这里, n=x 在lambda本地创建一个新的变量 n ,并在定义lambda时计算,使它具有与 x 在循环中该点相同的值。这意味着 n 的值在第一个lambda中为 0 ,在第二个lambda中为 1 ,在第三个中为 2 ,依此类推。因此每个lambda现在将返回正确的结果:

请注意,这种行为并不是lambda所特有的,但也适用于常规函数。

通常,不要使用 from modulename import * 。这样做会使导入器的命名空间变得混乱,并且使得连接器更难以检测未定义的名称。

在文件的顶部导入模块。这样做可以清楚地了解代码所需的其他模块,并避免了模块名称是否在范围内的问题。每行导入一个模块可以轻松添加和删除导入的模块,但每行导入多个模块会占用更少的屏幕空间。

如果按以下顺序导入模块,这是一种很好的做法:

有时需要将模块导入语句移动到函数或类里面,以避免循环导入问题。Gordon McMillan 说:

当两个模块都使用 "import <module>" 的导入形式时,循环导入就可以了。但是当第 2 个模块想从第 1 个模块中获取一个名称 ("from module import name") 并且导入位于顶层时,就会出错。 这是因为第 1 个模块中的名称还不可用,因为第 1 个模块正在忙着导入第 2 个模块。

在这种情况下,如果第二个模块仅用于一个函数,则可以轻松地将模块导入语句移动到该函数中。调用导入时,第一个模块将完成初始化,第二个模块可以进行导入。

如果某些模块是特定于平台的,则可能还需要将模块导入语句移出顶级代码。在这种情况下,甚至可能无法导入文件顶部的所有模块。在这种情况下,在相应的特定于平台的代码中导入正确的模块是一个很好的选择。

只有当需要解决诸如避免循环导入或试图减少模块初始化时间的问题时,才可以将导入移动到本地范围,例如在函数定义中。如果根据程序的执行方式,许多导入是不必要的,这种技术尤其有用。如果仅在某个函数中使用模块,您还可能希望将导入移到该函数中。请注意,第一次加载模块可能会因为模块的一次初始化而代价高昂,但多次加载模块实际上是免费的,只需进行几次字典查找。即使模块名称超出了作用域,模块也可能在

使用函数参数列表中的 *** 说明符收集参数;这会将位置参数作为元组,将关键字参数作为字典。然后,您可以使用 *** 调用另一个函数时传递这些参数:

是指出现在函数定义中的名称,而 则是在调用函数时实际传入的值。 形参定义了一个函数能接受何种类型的实参。 例如,对于以下函数定义:

如果你编写的代码就像下面一样:

你可能想知道为什么追加一个元素也改变了x。

产生这种结果有两个因素:

  1. 变量只是指向具体对象的名称。 执行 y = x 并不会为列表创建一个副本 —— 它只是创建了一个新变量 y 指向 x 所指向的同一对象。 这意味着只存在一个对象(列表),xy 都是对它的引用。

  2. 列表属于 对象,这意味着你可以改变它的内容。

在调用 append() 之后,这个可变对象的内容由 [] 变为 [10]。 由于两个变量都指向同一对象,因此使用任何一个名称所访问到的都是修改后的值 [10]

如果我们改为将不可变对象赋值给 x:

我们可以看到在此情况下 xy 就不再相等了。 这是因为整数是 对象,当我们执行 x = x + 1 时我们并不是改变了 5 这个对象的值;而是创建了一个新的对象 (整数 6) 并将其赋值给 x (也就是改变了 x 所指向的对象)。 在赋值之后我们就有了两个对象 (整数 65) 以及分别指向它们的两个变量

[10]sorted(y)) 则是创建新对象。 通常在 Python 中 (以及在标准库的所有代码中) 会改变原对象的方法将返回 None 以帮助避免混淆这两种不同类型的操作。 因此如果你错误地使用了 y.sort() 并期望它将返回一个经过排序的 y 的副本,你得到的结果将会是 None,这将导致你的程序产生一个容易诊断的错误。

但是,还存在一类操作,不同的类型执行相同的操作会有不同的行为:那就是增强赋值运算符。 例如,+= 会原地改变列表,但不会改变元组或整数 (a_list += [1, 2,

  • 如果我们有一个可变对象 (, , 等等),我们可以使用某些特定的操作来改变它,所有指向它的变量都会显示它的改变。

  • 如果我们有一个不可变对象 (, , 等等),所有指向它的变量都将显示相同样的值,但凡是会改变这个值的操作将总是返回一个新对象。

如果你想知道两个变量是否指向相同的对象,你可以使用 运算符,或内置函数 。

请记住在 Python 中参数是通过赋值来传递的。 由于赋值只是创建了对象的引用,因此在调用者和被调用者的参数名称之间没有别名,所以本身是没有按引用调用的。 你可以通过多种方式实现所需的效果。

  1. 通过返回一个结果元组:

    这几乎总是最清晰明了的解决方案。

  2. 通过使用全局变量。 这种方式不是线程安全的,而且也不受推荐。

  3. 通过传递一个可变 (即可原地修改的) 对象:

  4. 通过传递一个会被改变的字典:

  5. 或者在一个类实例中捆绑值:

    几乎没有任何适当理由将问题如此复杂化。

你的最佳选择是返回一个包含多个结果的元组。

你有两种选择:使用嵌套作用域,或者使用可调用对象。 例如,假设你想要定义 linear(a,b) 使其返回一个函数 f(x) 来设计 a*x+b 的值。 可以使用以下嵌套作用域:

或使用一个可调用对象:

可调用对象方式的缺点是速度略慢且生成的代码略长。 但是,请注意一组可调用对象能够通过继承来共享签名:

对象可以封装多个方法的状态:

一般来说,通常情况下请尝试 或 。 不是所有对象都可以复制,但多数都是可以的。

某些对象可以方便地复制。 例如字典具有 方法:

序列可以通过切片来复制:

对于一个用户自定义类的实例 x,dir(x) 将返回一个按字母顺序排序的包含实例属性和方法及其类所定义的属性名称的列表。

通常来说是做不到的,因为对象并不真正具有名称。 在本质上,赋值总是会将一个名称绑定到某个值;defclass 语句也是如此,但在这种情况下该值是一个可调用对象。 考虑以下代码:

不严谨地讲,该类有一个名称:虽然它是绑定了两个名称并通过名称 B 发起调用,所创建的实例仍然被视为类 A 的一个实例。 但是实例的名称则无法确定地说是 a 或是 b,因为有两个名称被绑定到了同一个值。

一般来说你的代码应该没有必要“知道”特定值的名称。 除非你是在编写特殊的内省程序,出现这样的问题通常表明如果改变方式可能会更有利。

跟你找出在你家门廊见到的某只猫的名字所用的办法一样:猫(对象)自己无法告诉你它的名字,它根本就不在乎 —— 所以找出它叫什么名字的唯一办法是问你的所有邻居(命名空间)那是不是他们的猫(对象)……

……并且如果你发现它有很多名字或根本没有名字也不必觉得惊讶!

逗号在 Python 中不是运算符。 考虑这个例子:

由于逗号不是运算符而是表达式之间的分隔符,以上代码的含义就相当于:

对于各种赋值运算符 (=, += 等) 来说同样如此。 它们并不是真正的运算符而是赋值语句中的语法分隔符。

有的。 相应语法如下:

在 Python 2.5 引入此语法之前,常见的做法是使用逻辑运算符:

然而这种做法并不保险,因为当 on_true 具有布尔假值时将会给出错误的结果。 所以,使用 ... if ... else ... 形式总是会更好。

可以。通常是在 中嵌套 lambda 来实现的。请参阅以下三个来自 Ulf Bartelt 的示例代码:

请不要在家里尝试,骚年!

函数参数列表中的斜杠表示在它之前的形参是仅限位置形参。 仅限位置形参没有外部可用的名称。 在调用接受仅限位置形参的函数时,参数只会基于它们的位置被映射到形参。 例如, 是一个接受仅限位置形参的函数。 它的文档是这样的:

在形参列表末尾的斜杠意味着两个形参都是仅限位置形参。 因此,附带关键字参数调用 将会导致报错:

要指定一个八进制数码,则在八进制值之前加一个零和一个小写或大写字母 "o" 作为前缀。 例如,要将变量 "a" 设为八进制的 "10" (十进制的 8),就输入:

十六进制数也同样简单。 只要在十六进制数之前加一个零和一个小写或大写字母 "x"。 十六进制数码中的字母可以为大写或小写。 例如在 Python 解释器中输入:

这主要是为了让 i % j 的正负与 j 一致,如果你想要这样的结果,并且又想要:

那么整除就必须向下取整。 C 同样要求保持一致,并且编译器在截短 i // j 的结果值时需要使 i % j 的正负与

对于 i % j 来说 j 为负值的应用场景实际上是非常少的。 而 j 为正值的情况则非常多,并且实际上在所有情况下让

对于整数,可使用内置的 类型构造器,例如 int('144') == 144。 类似地,可使用

== 324。 如果指定基数为 0,则按 Python 规则解读数字:前缀 '0o' 表示八进制,而 '0x' 表示十六进制。

如果你只是想将字符串转为数字,请不要使用内置函数 。 的速度会慢很多并且有安全风险:别人可能会传入具有你不想要的附带效果的 Python 表达式。 例如,别人可以传入 __import__('os').system("rm -rf $HOME") 这将删除你的家目录。

还具有将数字解读为 Python 表达式的效果,这样 eval('09') 将会导致语法错误,因为 Python 不允许十进制数的首位是 '0'

例如要将数字 144 转换为字符串 '144',可使用内置类型构造器 。 如果想要表示为十六进制或八进制数,可使用内置函数 或 。 想要更好地格式化,请参阅 和 等小节,例如

无法修改,因为字符串是不可变对象。 在大多数情况下,你应该使用你想要的各种部分来构造一个新字符串。 但是,如果你想要一个可以原地修改 Unicode 数据的对象,可尝试使用

  • 最好的做法是使用一个将字符串映射到函数的字典。 这一技巧的主要优势在于字符串不必与函数名称一致。 这也是用于模拟其他语言中 case 结构的主要技巧:

  • 请注意 可用于任何对象,包括类、类实例、模块等等。

    在标准库中多次使用了这个技巧,例如:

  • 使用 或 来解析出函数名:

    注意:使用 速度慢而且危险。 如果你不能绝对掌控字符串的内容,别人将能传入可被解析为任意函数直接执行的字符串。

可以使用 S.rstrip("\r\n") 从字符串 S 的末尾删除所有的换行符,而不删除其他尾随空格。如果字符串 S 表示多行,且末尾有几个空行,则将删除所有空行的换行符:

由于通常只在一次读取一行文本时才需要这样做,所以使用 S.rstrip() 这种方式工作得很好。

对于简单的输入解析,最方便的做法通常是使用字符串对象的 方法将一行内容拆解为以空格分隔的单词,然后使用 或 将表示十进制数的字符串转换为数值。 split() 支持可选的 "sep" 形参,适用于内容行使用空格符以外的分隔符的情况。

以于更复杂的输入解析,正则表达式会比 C 的 sscanf() 更强大,也更适合此类任务。

总的来说,这是个棘手的问题。首先,下面列出了深入了解前需要记住的事情:

  • 不同的 Python 实现具有不同的性能特点。 本 FAQ 着重解答的是 。

  • 行为可能因操作系统而异,尤其是在谈论 I / O 或多线程时。

  • 在尝试优化任何代码 ,应始终找到程序中的热点(请参阅 模块)。

  • 编写基准脚本将允许您在搜索改进时快速迭代(请参阅 模块)。

  • 强烈建议在可能引入隐藏在复杂优化中的回归之前,要有良好的代码覆盖率(通过单元测试或任何其他技术)。

话虽如此,加速Python代码有很多技巧。以下是一些可以达到可接受的性能水平的一般原则:

  • 使您的算法更快(或更改为更快的算法)可以产生比尝试在代码中使用微优化技巧更大的好处。

  • 使用正确的数据结构。参考文档 和 模块。

  • 当标准库提供用于执行某些操作的原语时,可能(尽管不能保证)比您可能提出的任何替代方案更快。对于用C编写的原语,例如内置函数和一些扩展类型,这是真的。例如,请确保使用 内置方法或相关的 函数进行排序(有关适度高级用法的示例,请参阅

  • 抽象倾向于创造间接性并迫使翻译更多地工作。如果间接级别超过完成的有用工作量,则程序将变慢。你应该避免过度抽象,特别是在微小的功能或方法的形式下(这通常也会对可读性产生不利影响)。

如果你已经达到纯 Python 允许的限制,那么有一些工具可以让你走得更远。 例如, 可以将稍微修改的 Python 代码版本编译为 C 扩展,并且可以在许多不同的平台上使用。 Cython 可以利用编译(和可选的类型注释)来使代码明显快于解释运行时的速度。 如果您对 C 编程技能有信心,也可以自己

对象是不可变的,因此将多个字符串连接在一起效率很低,因为每个连接都会创建一个新对象。在一般情况下,总运行时间是总字符串长度的二次方。

要连接多个 对象,通常推荐的用法是将它们放入一个列表中并在结尾处调用 :

(另一个合理有效的惯用方法是 )

要连接多个 对象,建议使用本地连接( += 运算符)扩展 对象:

类型构造器 tuple(seq) 可将任意序列(实际上是任意可迭代对象)转换为具有相同排列顺序的相同条目的元组。

('a', 'b', 'c')。 如果参数为一个元组,它不会创建副本而是返回同一对象,因此如果你不确定某个对象是否为元组时也可简单地调用 。

类型构造器 list(seq) 可将任意序列或可迭代对象转换为具有相同排列顺序的相同条目的列表。 例如,list((1, 2, 3)) 产生 [1,

Python 序列使用正数或负数作为序号或称索引号。 对于正数序号,第一个序号为 0 而 1 为第二个序号,依此类推。 对于负数序号,倒数第一个序号为 -1 而倒数第二个序号为 -2,依此类推。 可以认为 seq[-n] 就相当于

使用负数序号有时会很方便。 例如 S[:-1] 就是原字符串去掉最后一个字符,这可以用来移除某个字符串末尾的换行符。

这不会修改您的原始序列,而是构建一个反向顺序的新副本以进行迭代。

有关执行此操作的许多方法的详细讨论,请参阅 Python Cookbook:

如果您不介意重新排序列表,请对其进行排序,然后从列表末尾进行扫描,删除重复项:

如果列表的所有元素都可以用作设置键(即:它们都是 ),这通常会更快:

这会将列表转换为集合,从而删除重复项,然后返回到列表中。

对于删除重复项,一种可能的做法是设置删除条件显式地进行反向迭代。但是更容易也更快速的做法是进行隐式或显式的正向迭代并使用切片替代。 以下列出了三种方式。:

列表推导式可能是最快的。

列表在时间复杂度方面相当于C或Pascal数组;主要区别在于,python列表可以包含许多不同类型的对象。

array 模块还提供了创建具有紧凑表示的固定类型的数组的方法,但它的索引速度比列表慢。还要注意,数字扩展和其他扩展还定义了具有各种特性的类似数组的结构。

要获取Lisp样式的列表,可以使用元组模拟cons单元:

如果需要可变性,可以使用列表而不是元组。这里模拟lisp car的是 lisp_list[0] ,模拟cdr的是 lisp_list[1] 。只有在你确定真的需要的时候才这样做,因为它通常比使用Python列表慢得多。

你可能试图制作一个像这样的多维数组:

如果你打印它,看起来是正确的:

但是,当你给某一项赋值时,会同时在多个位置显示变化:

其中的原因在于使用 * 对列表执行重复操作并不是创建副本,它只是创建现有对象的引用。 *3 创建了对长度为二的同一列表的 3 个引用。 对某一行的改变会作用于所有行,通常这一定不是你所希望的。

建议的做法是先创建一个所需长度的列表,然后其中的元素再以一个新创建的列表来填充:

这样就生成了一个包含 3 个长度为二的不同列表的列表。 你也可以使用列表推导式:

或者你还可以使用提供矩阵类型的扩展包;其中最著名的是 。

这是由两个事实共同导致的结果,一是增强赋值运算符属于 赋值 运算符,二是在 Python 中存在可变和不可变两种不同的对象。

此处的讨论在任何对元组中指向可变对象的元素使用增强赋值运算符的情况都是普遍成立的,但在此我们只以 list+= 来举例。

发生异常的原因是显而易见的: 1 会与对象 a_tuple[0] 相加,而该对象为 (1),得到结果对象 2,但当我们试图将运算结果 2 赋值给元组的 0 号元素时就将报错,因为我们不能改变元组的元素所指向的对象。

在表层之处,以上增强赋值语句所做的大致是这样:

由于元组是不可变的,因此操作的赋值部分会引发错误。

发生异常会令人略感吃惊,还有一个更为令人吃惊的事实:虽然有报错,但是添加操作却生效了:

要明白为何会这样,你需要知道 (a) 如果一个对象实现了 __iadd__ 魔术方法,它会在执行 += 增强赋值时被调用,并且其返回值将用于该赋值语句; (b) 对于列表来说,__iadd__ 等价于在列表上调用 extend 并返回该列表。 因此对于列表我们可以说 += 就是 list.extend 的“快捷方式”:

a_list 所引用的对象已被修改,而引用被修改对象的指针又重新被赋值给 a_list。 赋值的最终结果没有变化,因为它是引用 a_list 之前所引用的同一对象的指针,但仍然发生了赋值操作。

因此,在我们的元组示例中,发生的事情等同于:

__iadd__ 成功执行,因此列表得到了扩充,但是虽然 result 指向了 a_tuple[0] 已经指向的同一对象,最后的赋值仍然导致了报错,因为元组是不可变的。

该技术归功于Perl社区的 Randal Schwartz,它通过将每个元素映射到其 "排序值(sort value)" 的度量对列表中的元素进行排序。在Python中,使用 方法的 key 参数:

将它们合并到元组的迭代器中,对结果列表进行排序,然后选择所需的元素。

最后一步的替代方案是:

如果你觉得这个更容易读懂,那么你可能更喜欢使用这个而不是前面的列表推导。然而,对于长列表来说,它的速度几乎是原来的两倍。为什么?首先, append() 操作必须重新分配内存,虽然它使用了一些技巧来避免每次都这样做,但它仍然偶尔需要这样做,而且代价相当高。第二,表达式 "result.append" 需要额外的属性查找。第三,必须执行所有这些函数调用会降低速度。

是通过执行类语句创建的特定对象类型。类对象 被当作模板来创建实例对象,实例对象包含了特定于数据类型的数据(属性)和代码(方法)。

类可以基于一个或多个的其他类,称之为基类(ES),它继承基类的属性和方法,这样就可以通过继承来连续地细化对象模型。例如:您可能有一个 Mailbox 类提供邮箱的基本访问方法.,它的子类 MboxMailbox,

方法 实际上就是类定义中的函数。对于某个对象 x 上的方法,通常称为 x.name(arguments...)

Self 只是 方法 的第一个参数的常规名称。例如:对于某个类的某个实例 x ,其方法 meth(self, a, b, c) 实际上应该被称为

请注意大多数程序不会经常对用户自定义类使用 。 如果是你自已开发的类,更正确的面向对象风格是在类中定义方法来封装特定的行为,而不是检查对象的类并根据它属于什么类来做不同的事。 例如,如果你有一个执行某些操作的函数:

更好的方法是在所有类上定义一个 search() 方法,然后调用它:

委托是一种面向对象的技巧(也称为设计模式)。 假设您有一个对象 x 并且想要改变其中一个方法的行为。 您可以创建一个新类,它提供您感兴趣的方法的新实现,并将所有其他方法委托给 x 的相应方法。

Python程序员可以轻松实现委托。 例如,以下类实现了一个类,该类的行为类似于文件,但将所有写入的数据转换为大写:

在这里 UpperOut 类重新定义了 write() 方法在调用下层的 self._outfile.write() 方法之前将参数字符串转换为大写形式。 所有其他方法都被委托给下层的 了解有关控制属性访问的更多信息。

请注意对于更一般的情况来说,委托可能包含更多细节问题。 当某些属性既需要读取又需要设置时,类还必须定义 方法,并且这样做必须小心谨慎。 的基本实现大致相当于以下代码:

来为自身保存局部状态而又不至于造成无限递归。

可以为基类定义别名,在类定义之前为其分配实际基类,并在整个类中使用别名。然后更改分配给别名的值,就能实现上述要求。顺便提一下,如果你想动态决定(例如,取决于资源的可用性)要使用哪个基类,这个技巧也很方便。例如:

Python支持静态数据和静态方法(在C ++或Java的意义上)。

对于静态数据,只需定义一个类属性。要为属性分配新值,就必须在赋值中显式使用类名:

c 自身,或者从 c.__class__ 回到 C 的基类搜索路径上的某个类所重载。

注意:在 C 的某个方法内部,像 self.count = 42 这样的赋值将在 self 自身的字典中新建一个名为 "count" 的不相关实例。 想要重新绑定类静态数据名称就必须总是指明类名,无论是在方法内部还是外部:

然而,获得静态方法效果的更直接的方法是通过一个简单的模块级函数:

如果您的代码是结构化的,以便为每个模块定义一个类(或紧密相关的类层次结构),那么这就提供了所需的封装。

这个答案实际上适用于所有方法,但问题通常首先出现在构造函数的上下文中。

在C ++中,你会这样写

在Python中,您必须编写一个构造函数,使用默认参数捕获所有情况。例如:

这不完全等同,但在实践中足够接近。

你也可以尝试一个可变长度的参数列表,例如:

相同的方法适用于所有方法定义。

以双下划线打头的变量会被“更名”以提供一种定义类私有变量的简单而有效的方式。 任何形式为 __spam 的标识符(至少前缀两个下划线,至多后缀一个下划线)文本会被替换为 _classname__spam,其中 classname 为去除了全部前缀下划线的当前类名称。

这并不能保证私密性:外部用户仍然可以访问 "_classname__spam" 属性,私有变量值也在对象的 __dict__ 中可见。 许多 Python 程序员从来都不使用这种私有变量名称。

del 语句不一定调用 —— 它只是减少对象的引用计数,如果(引用计数)达到零,才会调用 。

如果数据结构包含循环链接(例如,每个子级都有一个父级引用,每个父级都有一个子级列表的树),则引用计数将永远不会返回零。尽管Python 偶尔会运行一个算法来检测这样的循环,但在数据结构的引用计数清零后,垃圾收集器可能需要一段时间来运行,因此 方法可能会在不方便和随机的时间被调用。这对于重现一个问题,是非常不方便的。更糟糕的是,对象 的方法执行顺序是任意的。虽然可以运行 来强制回收,但在一些病态的情况下,对象永远不会被回收。

尽管有循环收集器,但在对象上定义一个显式的 close() 方法以便在用完之后调用它仍然是一个好主意。 这样 close() 方法可以随即删除引用子对象的属性。 不要直接调用 —— 应该由 调用 close(),并且 close() 能确保可以被同一对象多次地调用。

另一种避免循环引用的方法是使用 模块,该模块允许您指向对象而不增加其引用计数。例如,树状数据结构应该对其父级和同级引用使用弱引用(如果需要的话!)

最后,如果 方法引发异常,会将警告消息打印到 。

Python不跟踪类(或内置类型)的所有实例。您可以对类的构造函数进行编程,以通过保留每个实例的弱引用列表来跟踪所有实例。

返回一个整数,该整数在对象的生命周期内保证是唯一的。因为在CPython中,这是对象的内存地址,所以经常发生在从内存中删除对象之后,下一个新创建的对象被分配在内存中的相同位置。这个例子说明了这一点:

这两个id属于之前创建的不同整数对象,并在执行 id() 调用后立即删除。要确保要检查其id的对象仍处于活动状态,请创建对该对象的另一个引用:

当首次导入模块时(或当前已编译文件创建之后源文件发生了改动),在 .py 文件所在目录的 __pycache__ 子目录下会创建一个包含已编译代码的 .pyc 文件。该 .pyc 文件的名称开头部分将与 .py 文件名相同,并以 .pyc 为后缀,中间部分则依据创建它的 python 版本而各不相同。(详见 。)

无法创建 .pyc 文件的可能原因是包含源文件的目录存在权限问题,这意味着 __pycache__ 子目录无法被创建。 举例来说,如果你以某一用户来开发程序但以另一用户身份来运行程序时就可能发生问题,测试 Web 服务器就属于这种情况。

除非设置了 环境变量,否则当你导入模块并且 Python 具有创建 __pycache__ 子目录并将已编译模块写入该子目录的能力(权限、存储空间等等)时就会自动创建 .pyc 文件。

在最高层级运行的 Python 脚本不被视为导入,因此不会创建 .pyc 文件。 例如,如果你有一个最高层级模块文件 foo.py,它又导入了另一个模块 xyz.py,当你运行 xyz 是被导入的,但不会为 foo 创建 .pyc 文件,因为 foo.py 不是被导入的。

如果你需要为 foo 创建 .pyc 文件 —— 即为不是被导入的模块创建 .pyc 文件 —— 你可以使用 和 模块。

模块能够手动编译任意模块。 一种做法是交互式地使用该模块中的 compile() 函数:

这将会将 .pyc 文件写入与 foo.py 相同位置下的 __pycache__ 子目录(或者你也可以通过可选参数 cfile 来重载该行为)。

你还可以使用 模块自动编译一个目录或多个目录下的所有文件。 具体做法可以是在命令行提示符中运行

模块可以通过查看预定义的全局变量 __name__ 找到自己的模块名称。如果它的值为 '__main__' ,程序将作为脚本运行。通常,通过导入使用的许多模块也提供命令行界面或自检,并且只在检查 __name__ 之后,才执行之后的代码:

问题是解释器将执行以下步骤:

  • 创建用于foo的空全局变量

  • foo被编译并开始执行

  • 创建了用于bar 的空全局变量

  • bar被编译并开始执行

  • bar导入foo(这是一个空操作(no-op ),因为已经有一个名为foo的模块)

最后一步失败了,因为Python还没有解释foo,而foo的全局符号字典仍然是空的。

当你使用 import foo ,然后尝试在全局代码中访问 foo.foo_var 时,会发生同样的事情。

这个问题有(至少)三种可能的解决方法。

Guido van Rossum 建议避免使用 from <module> import ... ,并将所有代码放在函数中。全局变量和类变量的初始化只能使用常量或内置函数。这意味着导入模块中的所有内容都被引用为

Jim Roskind建议在每个模块中按以下顺序执行步骤:

  • 导出(全局变量,函数和不需要导入基类的类)

  • 活动代码(包括从导入值初始化的全局变量)。

van Rossum不喜欢这种方法,因为导入出现在一个陌生的地方,但这种方法确实有效。

Matthias Urlichs建议重构代码,以便首先不需要递归导入。

这些解决方案并不相互排斥。

考虑使用 中的函数 :

出于效率和一致性的原因,Python仅在第一次导入模块时读取模块文件。如果不这么做,在一个由许多模块组成的程序中,每个模块都会导入相同的基本模块,那么基本模块将被解析和重新解析多次。要强制重新读取已更改的模块,请执行以下操作:

警告:这种技术不是100%万无一失。特别是包含如下语句的模块

将继续使用旧版本的导入对象。如果模块包含类定义,则不会更新现有的类实例以使用新的类定义。这可能导致以下矛盾行为:

如果打印出类对象的“标识”,问题的本质就会明确:

我要回帖

更多关于 python中的range函数 的文章

 

随机推荐