Programming FAQ

内容

General Questions

Is there a source code level debugger with breakpoints, single-stepping, etc.?

是。

pdb模块是一个简单但足够的用于Python的控制台模式调试器。It is part of the standard Python library, and is documented in the Library Reference Manual. 您还可以使用pdb的代码作为示例编写自己的调试器。

IDLE交互式开发环境是标准Python发行版(通常可用作Tools / scripts / idle)的一部分,包括一个图形调试器。

PythonWin是一个包含基于pdb的GUI调试器的Python IDE。Pythonwin调试器颜色断点和有很多很酷的功能,如调试非Pythonwin程序。Pythonwin作为Python for Windows Extensions项目的一部分提供,并作为ActivePython发行版的一部分(参见https://www.activestate.com/activepython)。

Boa Constructor是一个使用wxWidgets的IDE和GUI构建器。它提供了可视化框架创建和操作,对象检查器,源代码上的许多视图,如对象浏览器,继承层次结构,doc字符串生成的html文档,高级调试器,集成帮助和Zope支持。

Eric是一个基于PyQt和Scintilla编辑组件的IDE。

Pydb是标准Python调试器pdb的一个版本,修改为用于DDD(数据显示调试器),一个流行的图形调试器前端。Pydb可以在http://bashdb.sourceforge.net/pydb/找到,DDD可以在https://www.gnu.org/software/ddd

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

Is there a tool to help find bugs or perform static analysis?

是。

PyChecker是一个静态分析工具,它在Python源代码中发现错误,并警告代码复杂性和风格。您可以从http://pychecker.sourceforge.net/获取PyChecker。

Pylint是另一个用于检查模块是否满足编码标准的工具,还可以编写插件以添加自定义特征。除了PyChecker执行的错误检查外,Pylint还提供一些附加功能,例如检查行长度,变量名称是否根据编码标准格式良好,是否已完全实现已声明的接口等。https://docs.pylint.org/提供了Pylint功能的完整列表。

How can I create a stand-alone binary from a Python script?

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

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

它通过以下方式工作:递归地扫描源代码以导入语句(两种形式),并在标准Python路径以及源目录(对于内建模块)中查找模块。然后将Python编写的模块的字节码转换为C代码(可以使用marshal模块将数组初始化器转换为代码对象),并创建一个仅包含程序中实际使用的内建模块的定制配置文件。然后编译生成的C代码,并将其链接到Python解释器的其余部分,以形成一个自包含的二进制文件,其行为完全像你的脚本。

显然,冻结需要一个C编译器。还有几个其他的实用程序不。一个是Thomas Heller的py2exe(仅限Windows)

另一个工具是Anthony Tuininga的cx_Freeze

Are there coding standards or a style guide for Python programs?

是。标准库模块所需的编码风格记录为 PEP 8

Core Language

Why am I getting an UnboundLocalError when the variable has a value?

在通过在函数体中的某处添加赋值语句来修改UnboundLocalError时,在以前工作的代码中获取UnboundLocalError可能是一个惊喜。

这段代码:

>>> x = 10
>>> def bar():
...     print(x)
>>> bar()
10

工程,但是这段代码:

>>> x = 10
>>> def foo():
...     print(x)
...     x += 1

会导致UnboundLocalError:

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment

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

在上面的示例中,您可以通过声明外部变量global:

>>> x = 10
>>> def foobar():
...     global x
...     print(x)
...     x += 1
>>> foobar()
10

这个显式声明是必需的,以提醒您(与类和实例变量的表面类似情况不同),实际上是修改外部范围中的变量的值:

>>> print(x)
11

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

>>> def foo():
...    x = 10
...    def bar():
...        nonlocal x
...        print(x)
...        x += 1
...    bar()
...    print(x)
>>> foo()
10
11

What are the rules for local and global variables in Python?

在Python中,只在函数内引用的变量是隐式全局变量。如果一个变量在函数体内的任何地方被赋值,它被假定为一个局部变量,除非明确声明为全局变量。

虽然起初有点令人惊讶,一个时刻的考虑解释这一点。一方面,对于分配的变量,需要global提供了一个阻止无意的副作用的条形码。另一方面,如果所有全局引用都需要global,则您将始终使用global您必须声明每个对内建函数或引入模块的组件的引用。这种混乱会破坏用于识别副作用的global声明的有用性。

Why do lambdas defined in a loop with different values all return the same result?

假设你使用一个for循环来定义一些不同的lambda(或者甚至是plain函数)。

>>> squares = []
>>> for x in range(5):
...     squares.append(lambda: x**2)

这给了一个列表,其中包含5个计算x**2的lambda。您可能希望在调用时分别返回014916但是,当你实际尝试,你会看到他们都返回16

>>> squares[2]()
16
>>> squares[4]()
16

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

>>> x = 8
>>> squares[2]()
64

为了避免这种情况,您需要将值保存在lambdas本地的变量中,以使它们不依赖于全局x的值:

>>> squares = []
>>> for x in range(5):
...     squares.append(lambda n=x: n**2)

这里,n=x创建一个新的变量n局部于lambda,并在定义lambda时计算,使其具有x这意味着n的值将在第一个λ中为0,在第二个2中为1第三,等等。因此,每个lambda将返回正确的结果:

>>> squares[2]()
4
>>> squares[4]()
16

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

How do I share global variables across modules?

在单个程序中跨模块共享信息的规范方式是创建一个特殊模块(通常称为config或cfg)。只需在应用程序的所有模块中导入配置模块;该模块将变为可用的全局名称。因为每个模块只有一个实例,对模块对象所做的任何更改都会反映到任何地方。例如:

config.py:

x = 0   # Default value of the 'x' configuration setting

mod.py:

import config
config.x = 1

main.py:

import config
import mod
print(config.x)

注意,使用模块也是实现Singleton设计模式的基础,同样的原因。

What are the “best practices” for using import in a module?

一般来说,请不要使用 t> 模块名 导入 *中的这样做会干扰进口商的命名空间,并使得linters更难以检测未定义的名称。

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

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

  1. 标准库模块。sysosgetoptre
  2. 第三方库模块(任何安装在Python的site-packages目录中)。mx.DateTime,ZODB,PIL.Image等。
  3. 本地开发的模块

有时需要将导入移动到函数或类,以避免循环导入的问题。Gordon McMillan说:

Circular imports are fine where both modules use the “import <module>” form of import. They fail when the 2nd module wants to grab a name out of the first (“from module import name”) and the import is at the top level. That’s because names in the 1st are not yet available, because the first module is busy importing the 2nd.

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

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

只有在需要解决诸如避免循环导入或尝试减少模块的初始化时间等问题时,才将导入移动到本地作用域(例如函数定义内部)。如果许多导入是不必要的,这种技术尤其有用,这取决于程序的执行方式。如果模块只在该函数中使用,则可能还需要将导入移动到函数中。注意,第一次加载模块可能是昂贵的,因为模块的一次性初始化,但是多次加载模块几乎是自由的,仅花费几个字典查找。即使模块名称超出范围,该模块可能在sys.modules中可用。

Why are default values shared between objects?

这种类型的bug通常咬住新手程序员。考虑这个功能:

def foo(mydict={}):  # Danger: shared reference to one dict for all calls
    ... compute something ...
    mydict[key] = value
    return mydict

第一次调用此函数时,mydict包含单个项目。第二次,mydict包含两个项目,因为当foo()开始执行时,mydict开始于其中已有的项目。

通常期望函数调用为默认值创建新对象。这不是会发生什么。在定义函数时,默认值只创建一次。如果该对象被改变,像这个例子中的字典,对该函数的后续调用将引用这个改变的对象。

根据定义,不可变对象(如数字,字符串,元组和None)无法更改。对可变对象(如字典,列表和类实例)的更改可能会导致混乱。

由于这个特性,良好的编程习惯不使用可变对象作为默认值。而应使用None作为默认值,并在函数内部检查参数是否None并创建新的列表/字典/如果是的话。例如,不要写:

def foo(mydict={}):
    ...

但:

def foo(mydict=None):
    if mydict is None:
        mydict = {}  # create a new dict for local namespace

此功能可能有用。当你有一个计算耗时的函数时,一种常见的技术是缓存函数的每次调用的参数和结果值,如果再次请求相同的值,则返回缓存的值。这被称为“记忆”,并且可以这样实现:

# Callers will never provide a third parameter for this function.
def expensive(arg1, arg2, _cache={}):
    if (arg1, arg2) in _cache:
        return _cache[(arg1, arg2)]

    # Calculate the value
    result = ... expensive computation ...
    _cache[(arg1, arg2)] = result           # Store result in the cache
    return result

您可以使用包含字典的全局变量,而不是默认值;这是一个味道的问题。

How can I pass optional or keyword parameters from one function to another?

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

def f(x, *args, **kwargs):
    ...
    kwargs['width'] = '14.3c'
    ...
    g(x, *args, **kwargs)

What is the difference between arguments and parameters?

Parameters由出现在函数定义中的名称定义,而arguments是调用函数时实际传递的值。参数定义函数可以接受的参数类型。例如,给定函数定义:

def func(foo, bar=None, **kwargs):
    pass

foobarkwargsfunc的参数。但是,当调用func时,例如:

func(42, bar=314, extra=somevar)

42314somevar是参数。

Why did changing list ‘y’ also change list ‘x’?

如果你编写代码:

>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]

您可能会想知道为什么在y中追加一个元素也会改变x

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

  1. 变量只是指对象的名称。执行y = x不创建列表的副本 - 它创建一个新变量y指代同一对象x这意味着只有一个对象(列表),并且xy都指向它。
  2. 列表为mutable,这意味着您可以更改其内容。

调用append()之后,可变对象的内容已从[]更改为[10]由于这两个变量引用相同的对象,因此使用任一名称访问修改的值[10]

如果我们为x指定一个不可变对象:

>>> x = 5  # ints are immutable
>>> y = x
>>> x = x + 1  # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5

我们可以看到在这种情况下,xy不再相等。这是因为整数immutable,当我们做x = x / t6> 1我们不是通过递增其值来改变int 5相反,我们正在创建一个新对象(int 6)并将其赋值给x(即改变哪个对象xAfter this assignment we have two objects (the ints 6 and 5) and two variables that refer to them (x now refers to 6 but y still refers to 5).

Some operations (for example y.append(10) and y.sort()) mutate the object, whereas superficially similar operations (for example y = y + [10] and sorted(y)) create a new object. 一般来说,在Python中(在所有情况下在标准库中),一个变化一个对象的方法将返回None,以帮助避免混淆这两种类型的操作。所以如果你错误地写了y.sort(),它会给你一个y的排序副本,你会得到None ,这可能会导致您的程序生成一个容易诊断的错误。

然而,有一类操作,其中相同的操作有时具有不同的行为,具有不同的类型:增强的赋值操作符。For example, += mutates lists but not tuples or ints (a_list += [1, 2, 3] is equivalent to a_list.extend([1, 2, 3]) and mutates a_list, whereas some_tuple += (1, 2, 3) and some_int += 1 create new objects).

换一种说法:

  • 如果我们有一个可变对象(listdictset等)。),我们可以使用一些特定的操作来改变它,并且引用它的所有变量都会看到变化。
  • 如果我们有一个不可变对象(strinttuple等)。),引用它的所有变量将始终看到相同的值,但将该值转换为新值的操作总是返回一个新对象。

如果你想知道两个变量是否指向同一个对象,你可以使用is操作符,或内建函数id()

How do I write a function with output parameters (call by reference)?

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

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

    def func2(a, b):
        a = 'new-value'        # a and b are local names
        b = b + 1              # assigned to new objects
        return a, b            # return new values
    
    x, y = 'old-value', 99
    x, y = func2(x, y)
    print(x, y)                # output: new-value 100
    

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

  2. 通过使用全局变量。这不是线程安全的,不推荐。

  3. 通过传递一个可变的(可变的就地)对象:

    def func1(a):
        a[0] = 'new-value'     # 'a' references a mutable list
        a[1] = a[1] + 1        # changes a shared object
    
    args = ['old-value', 99]
    func1(args)
    print(args[0], args[1])    # output: new-value 100
    
  4. 通过传入一个获得变异的字典:

    def func3(args):
        args['a'] = 'new-value'     # args is a mutable dictionary
        args['b'] = args['b'] + 1   # change it in-place
    
    args = {'a': 'old-value', 'b': 99}
    func3(args)
    print(args['a'], args['b'])
    
  5. 或捆绑类中的值实例:

    class callByRef:
        def __init__(self, **args):
            for (key, value) in args.items():
                setattr(self, key, value)
    
    def func4(args):
        args.a = 'new-value'        # args is a mutable callByRef
        args.b = args.b + 1         # change object in-place
    
    args = callByRef(a='old-value', b=99)
    func4(args)
    print(args.a, args.b)
    

    几乎没有一个很好的理由让这个复杂。

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

How do you make a higher order function in Python?

您有两个选择:可以使用嵌套的作用域,也可以使用可调用的对象。For example, suppose you wanted to define linear(a,b) which returns a function f(x) that computes the value a*x+b. 使用嵌套作用域:

def linear(a, b):
    def result(x):
        return a * x + b
    return result

或者使用可调用对象:

class linear:

    def __init__(self, a, b):
        self.a, self.b = a, b

    def __call__(self, x):
        return self.a * x + self.b

在这两种情况下,

taxes = linear(0.3, 2)

给出一个可调用对象,其中税(10e6) == 0.3 * 10e6 + 2

可调用对象方法的缺点是它有点慢,导致稍长的代码。然而,请注意,一个容器的callables可以通过继承共享其声明:

class exponential(linear):
    # __init__ inherited
    def __call__(self, x):
        return self.a * (x ** self.b)

对象可以封装几种方法的状态:

class counter:

    value = 0

    def set(self, x):
        self.value = x

    def up(self):
        self.value = self.value + 1

    def down(self):
        self.value = self.value - 1

count = counter()
inc, dec, reset = count.up, count.down, count.set

这里inc()dec()reset() act类似共享相同计数变量的函数。

How do I copy an object in Python?

一般来说,对于一般情况,请尝试copy.copy()copy.deepcopy()不是所有的对象都可以复制,但大多数都可以。

某些对象可以更容易地复制。字典有一个copy()方法:

newdict = olddict.copy()

序列可以通过切片复制:

new_l = l[:]

How can I find the methods or attributes of an object?

对于用户定义类的实例x,dir(x)返回包含实例属性的名称的字母表列表,以及由其类定义的方法和属性。

How can my code discover the name of an object?

一般来说,它不能,因为对象不真的有名字。基本上,赋值总是将一个名字绑定到一个值;对于defclass语句也是如此,但在这种情况下,该值是可调用的。考虑下面的代码:

>>> class A:
...     pass
...
>>> B = A
>>> a = B()
>>> b = a
>>> print(b)
<__main__.A object at 0x16D07CC>
>>> print(a)
<__main__.A object at 0x16D07CC>

可以说这个类有一个名称:即使它绑定到两个名称并通过名称B调用,创建的实例仍然被报告为类A的实例。然而,不可能说实例的名称是a还是b,因为这两个名称都绑定到相同的值。

一般来说,你的代码不需要“知道特定值的名称”。除非你是故意写内省程序,这通常表明改变方法可能是有益的。

在comp.lang.python中,Fredrik Lundh曾经给出了一个很好的类比来回答这个问题:

The same way as you get the name of that cat you found on your porch: the cat (object) itself cannot tell you its name, and it doesn’t really care – so the only way to find out what it’s called is to ask all your neighbours (namespaces) if it’s their cat (object)...

....and don’t be surprised if you’ll find that it’s known by many names, or no name at all!

What’s up with the comma operator’s precedence?

逗号不是Python中的操作符号。考虑这个会话:

>>> "a" in "b", "a"
(False, 'a')

由于逗号不是操作符,而是表达式之间的分隔符,上面是计算,就像你输入:

("a" in "b"), "a"

不:

"a" in ("b", "a")

各种赋值运算符(=+=等)也是如此。它们不是真正的运算符,而是赋值语句中的句法分隔符。

Is there an equivalent of C’s ”?:” ternary operator?

就在这里。语法如下:

[on_true] if [expression] else [on_false]

x, y = 50, 25
small = x if x < y else y

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

[expression] and [on_true] or [on_false]

然而,这个习语是不安全的,因为当on_true有一个假的布尔值时,它可能会给出错误的结果。因此,总是最好使用... if ... else ...

Is it possible to write obfuscated one-liners in Python?

是。通常这是通过在lambda内嵌套lambda来实现的。看到以下三个例子,由于Ulf Bartelt:

from functools import reduce

# Primes < 1000
print(list(filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))))

# First 10 Fibonacci numbers
print(list(map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1:
f(x,f), range(10))))

# Mandelbrot set
print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,
Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,
Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,
i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y
>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(
64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy
))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24))
#    \___ ___/  \___ ___/  |   |   |__ lines on screen
#        V          V      |   |______ columns on screen
#        |          |      |__________ maximum of "iterations"
#        |          |_________________ range on y axis
#        |____________________________ range on x axis

不要在家里试试,孩子们!

Numbers and strings

How do I specify hexadecimal and octal integers?

要指定一个八进制数字,在八进制值前面加一个零,然后是一个小写或大写的“o”。例如,要将变量“a”设置为八进制值“10”(十进制为8),请键入:

>>> a = 0o10
>>> a
8

十六进制也是一样容易。只需在十六进制数前面加一个零,然后是一个小写或大写的“x”。十六进制数字可以指定为小写或大写。例如,在Python解释器中:

>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178

Why does -22 // 10 return -3?

主要是由于i jj如果你想要,也想要:

i == (i // j) * j + (i % j)

那么整数除法必须返回floor。C还需要保持标识,然后截断i // j的编译器需要使 i ji具有相同的符号。

j为负时,i j的实际用例很少。j为正时,有很多,并且实际上所有这些对于i / t5>> = 0如果时钟说10现在,它说什么200小时前?-190 12 == 2 有用; -190 12 == -10是一个等待咬的bug。

How do I convert a string to a number?

对于整数,使用内建int()类型构造函数,例如。int('144') == 144类似地,float()转换为浮点数,例如。float('144') == 144.0

默认情况下,这些将数字解释为十进制,以便int('0144') == 144 int('0x144')引发ValueErrorint(string, base)将基础转换为第二个可选参数,所以int ', 16) == 324如果base被指定为0,则使用Python的规则来解释数字:前导'0o'表示八进制,'0x'表示十六进制数。

如果你需要的是将字符串转换为数字,不要使用内建函数eval()eval()会明显变慢,并且会带来安全风险:有人可能传递一个可能有不必要的副作用的Python表达式。例如,有人可以通过__ import __('os')。system(“rm -rf $ HOME”) t0 >这将擦除您的主目录。

eval()还具有将数字解释为Python表达式的效果,eval('09')给出语法错误,因为Python不允许十进制数(除'0'除外)中的前导'0'。

How do I convert a number to a string?

要将例如数字144转换为字符串'144',请使用内建类型构造函数str()如果想要十六进制或八进制表示,请使用内建函数hex()oct()有关花式格式,请参阅Format String Syntax部分,例如"{:04d}".format(144) yields '0144' and "{:.3f}".format(1.0/3.0) yields '0.333'.

How do I modify a string in place?

你不能,因为字符串是不可变的。在大多数情况下,您应该简单地从要组装它的各个部分构造一个新的字符串。但是,如果您需要一个能够修改现场unicode数据的对象,请尝试使用io.StringIO对象或array模块:

>>> import io
>>> s = "Hello, world"
>>> sio = io.StringIO(s)
>>> sio.getvalue()
'Hello, world'
>>> sio.seek(7)
7
>>> sio.write("there!")
6
>>> sio.getvalue()
'Hello, there!'

>>> import array
>>> a = array.array('u', s)
>>> print(a)
array('u', 'Hello, world')
>>> a[0] = 'y'
>>> print(a)
array('u', 'yello, world')
>>> a.tounicode()
'yello, world'

How do I use strings to call functions/methods?

有各种技术。

  • 最好是使用将字符串映射到函数的字典。这种技术的主要优点是字符串不需要匹配函数的名称。这也是用于模拟案例构造的主要技术:

    def a():
        pass
    
    def b():
        pass
    
    dispatch = {'go': a, 'stop': b}  # Note lack of parens for funcs
    
    dispatch[get_input()]()  # Note trailing parens to call function
    
  • 使用内建函数getattr()

    import foo
    getattr(foo, 'bar')()
    

    请注意,getattr()适用于任何对象,包括类,类实例,模块等。

    这在标准库中的几个地方使用,像这样:

    class Foo:
        def do_foo(self):
            ...
    
        def do_bar(self):
            ...
    
    f = getattr(foo_instance, 'do_' + opname)
    f()
    
  • 使用locals()eval()来解析函数名称:

    def myFunc():
        print("hello")
    
    fname = "myFunc"
    
    f = locals()[fname]
    f()
    
    f = eval(fname)
    f()
    

    注意:使用eval()很慢且很危险。如果你没有绝对的控制字符串的内容,有人可以传递一个字符串,导致一个任意的函数被执行。

Is there an equivalent to Perl’s chomp() for removing trailing newlines from strings?

您可以使用S.rstrip("\r\n")从字符串S结束处删除所有行终止符,而不删除其他结尾空格。如果字符串S表示多行,在末尾有多个空行,则将删除所有空行的行终止符:

>>> lines = ("line 1 \r\n"
...          "\r\n"
...          "\r\n")
>>> lines.rstrip("\n\r")
'line 1 '

因为这通常只在每次读取文本一行时才需要,所以使用S.rstrip()这种方式很好。

Is there a scanf() or sscanf() equivalent?

不是这样的。

对于简单的输入解析,最简单的方法通常是使用字符串对象的split()方法将行拆分为空格分隔的字,然后使用int()float()split()支持一个可选的“sep”参数,如果该行使用除空格之外的其他字符作为分隔符,那么此参数非常有用。

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

Performance

My program is too slow. How do I speed it up?

这是一个艰难的一般,。首先,下面是潜水前要记住的事项:

  • Python实现的性能特性不同。此常见问题集中于CPython
  • 不同操作系统的行为可能不同,特别是在谈论I / O或多线程时。
  • 在尝试优化任何代码之前,应始终在程序中找到热点(请参阅profile模块)。
  • 编写基准脚本将允许您在搜索改进时快速迭代(请参阅timeit模块)。
  • 强烈建议在潜在引入隐藏在复杂优化中的回归之前具有良好的代码覆盖率(通过单元测试或任何其他技术)。

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

  • 使算法更快(或更快更快)可以产生比尝试在您的代码上微微优化技巧更大的好处。
  • 使用正确的数据结构。研究Built-in Typescollections模块的文档。
  • 当标准库提供了做某事的原语时,它很可能(虽然不能保证)比任何可能出现的替代更快。对于用C语言编写的原语,例如内置和一些扩展类型,这是双重的。例如,确保使用list.sort()内建方法或相关的sorted()函数进行排序(并参阅Sorting HOW TO中的高级用法示例)。
  • 抽象倾向于产生间接并迫使解释器工作更多。如果间接级别超过有用的工作量,你的程序会更慢。你应该避免过多的抽象,特别是在微小的函数或方法的形式(这也往往是对可读性的损害)。

如果你已经达到了纯Python可以允许的极限,有一些工具可以让你更远。例如,Cython可以将稍微修改的Python代码编译成C扩展,并且可以在许多不同的平台上使用。Cython可以利用编译(和可选类型注释)使代码明显比解释时更快。如果你对自己的C编程技能有信心,你也可以write a C extension module

也可以看看

wiki页面专门用于性能提示

What is the most efficient way to concatenate many strings together?

strbytes对象是不可变的,因此将许多字符串连接在一起是无效的,因为每个连接创建一个新对象。在一般情况下,总运行时成本是总字符串长度的二次方。

要累积许多str对象,推荐的方法是将它们放入列表中,并在结尾调用str.join()

chunks = []
for s in my_strings:
    chunks.append(s)
result = ''.join(chunks)

(另一个相当高效的习语是使用io.StringIO

为了累积许多bytes对象,推荐的习语是使用就地并置(+=操作符)来扩展bytearray

result = bytearray()
for b in my_bytes_objects:
    result += b

Sequences (Tuples/Lists)

How do I convert between tuples and lists?

类型构造函数tuple(seq)将任何序列(实际上,任何可迭代的)转换为具有相同项目的元组。

For example, tuple([1, 2, 3]) yields (1, 2, 3) and tuple('abc') yields ('a', 'b', 'c'). 如果参数是一个元组,它不会做一个副本,但返回相同的对象,所以当你不确定一个对象已经是一个元组时,调用tuple()是很便宜的。

类型构造函数list(seq)将任何序列或可迭代转换为具有相同顺序的相同项目的列表。For example, list((1, 2, 3)) yields [1, 2, 3] and list('abc') yields ['a', 'b', 'c']. 如果参数是一个列表,它会像seq[:]那样进行复制。

What’s a negative index?

Python序列用正数和负数索引。对于正数0是第一个索引1是第二个索引等等。对于负指数-1是最后一个指数,-2是倒数第二个(倒数第二个)指数等等。认为seq[-n]seq[len(seq)-n]相同。

使用负指数可以非常方便。例如S[:-1]是除了最后一个字符之外的所有字符串,这对于从字符串中删除尾部换行很有用。

How do I iterate over a sequence in reverse order?

使用reversed()内建函数,这是Python 2.4中的新功能:

for x in reversed(sequence):
    ...  # do something with x ...

这将不会触及您的原始序列,但构建一个新的副本与颠倒的顺序来迭代。

使用Python 2.3,您可以使用扩展的片语法:

for x in sequence[::-1]:
    ...  # do something with x ...

How do you remove duplicates from a list?

有关许多方法的详细讨论,请参阅Python Cookbook:

如果您不介意重新排序列表,请对其进行排序,然后从列表的末尾开始扫描,因此在您删除重复的时候:

if mylist:
    mylist.sort()
    last = mylist[-1]
    for i in range(len(mylist)-2, -1, -1):
        if last == mylist[i]:
            del mylist[i]
        else:
            last = mylist[i]

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

mylist = list(set(mylist))

这将列表转换为一个集合,从而删除重复,然后回到列表。

How do you make an array in Python?

使用列表:

["this", 1, "is", "an", "array"]

列表在时间复杂性上等同于C或Pascal数组;主要区别是Python列表可以包含许多不同类型的对象。

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

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

lisp_list = ("like",  ("this",  ("example", None) ) )

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

How do I create a multidimensional list?

你可能试图像这样做一个多维数组:

>>> A = [[None] * 2] * 3

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

>>> A
[[None, None], [None, None], [None, None]]

但是,当您分配值时,它会显示在多个地方:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

原因是,使用*复制列表不会创建副本,它只创建对现有对象的引用。*3创建一个列表,其中包含对长度为2的相同列表的3个引用。对一行的更改将显示在所有行,这几乎肯定不是你想要的。

建议的方法是首先创建所需长度的列表,然后使用新创建的列表填充每个元素:

A = [None] * 3
for i in range(3):
    A[i] = [None] * 2

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

w, h = 2, 3
A = [[None] * w for i in range(h)]

或者,您可以使用提供矩阵数据类型的扩展; NumPy是最知名的。

How do I apply a method to a sequence of objects?

使用列表推导式:

result = [obj.method() for obj in mylist]

Why does a_tuple[i] += [‘item’] raise an exception when the addition works?

这是因为扩充赋值操作符是赋值运算符,以及Python中可变对象和不可变对象之间的差异。

当将增强的赋值运算符应用于指向可变对象的元组的元素时,本讨论通常适用,但是我们将使用list+=作为我们的示例。

如果你写道:

>>> a_tuple = (1, 2)
>>> a_tuple[0] += 1
Traceback (most recent call last):
   ...
TypeError: 'tuple' object does not support item assignment

The reason for the exception should be immediately clear: 1 is added to the object a_tuple[0] points to (1), producing the result object, 2, but when we attempt to assign the result of the computation, 2, to element 0 of the tuple, we get an error because we can’t change what an element of a tuple points to.

在封面下,这个增强的赋值语句所做的大约是这样的:

>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

它是产生错误的操作的赋值部分,因为元组是不可变的。

当你写如下:

>>> a_tuple = (['foo'], 'bar')
>>> a_tuple[0] += ['item']
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

例外是有点更令人惊讶,甚至更令人惊讶的是,即使有一个错误,append工作:

>>> a_tuple[0]
['foo', 'item']

要了解为什么会发生这种情况,你需要知道(a)如果一个对象实现了一个__iadd__魔法,它会在执行+=它的返回值是在赋值语句中使用的;和(b)对于列表,__iadd__等效于在列表上调用extend并返回列表。这就是为什么我们说对于列表,+=list.extend的“速记”

>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]

这相当于:

>>> result = a_list.__iadd__([1])
>>> a_list = result

a_list指向的对象已经改变,并且指向变异对象的指针返回到a_list分配的最终结果是无操作,因为它是指向a_list先前指向的同一对象的指针,但分配仍会发生。

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

>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

__iadd__成功,因此列表被扩展,但即使result指向a_tuple[0]

Dictionaries

I want to do a complicated sort: can you do a Schwartzian Transform in Python?

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

Isorted = L[:]
Isorted.sort(key=lambda s: int(s[10:15]))

How can I sort one list by values from another list?

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

>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs = sorted(pairs)
>>> pairs
[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
>>> result = [x[1] for x in pairs]
>>> result
['else', 'sort', 'to', 'something']

最后一步的替代方法是:

>>> result = []
>>> for p in pairs: result.append(p[1])

如果你觉得这更清晰,你可能更喜欢使用这个而不是最终的列表推导式。然而,对于长列表,它几乎是慢两倍。为什么?首先,append()操作必须重新分配内存,虽然它每次都使用一些技巧来避免这样做,但它仍然必须偶尔进行,这会花费很多。第二,表达式“result.append”需要额外的属性查找,第三,从所有这些函数调用中减少速度。

Objects

What is a class?

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

类可以基于一个或多个其他类,称为其基类。然后它继承其基类的属性和方法。这允许通过继承来连续地精化对象模型。您可能有一个通用的Mailbox类,为邮箱提供基本的访问器方法,以及MboxMailboxMaildirMailboxOutlookMailbox

What is a method?

方法是某些对象x上的函数,通常称为x.name(arguments...)方法被定义为类定义中的函数:

class C:
    def meth(self, arg):
        return arg * 2 + self.attribute

What is self?

Self只是方法的第一个参数的常规名称。A method defined as meth(self, a, b, c) should be called as x.meth(a, b, c) for some instance x of the class in which the definition occurs; the called method will think it is called as meth(x, a, b, c).

另见Why must ‘self’ be used explicitly in method definitions and calls?

How do I check if an object is an instance of a given class or of a subclass of it?

使用内建函数isinstance(obj, cls)您可以通过提供元组而不是单个类来检查对象是否是多个类中的任何一个的实例,例如isinstance(obj, (class1, class2, ...)) 并且还可以检查对象是否为Python的内建类型之一,例如isinstance(obj, str) or isinstance(obj, (int, float, complex)).

请注意,大多数程序不经常使用用户定义类上的isinstance()如果你自己开发类,一个更适当的面向对象的风格是定义封装特定行为的类的方法,而不是检查对象的类,并做一个不同的事情,基于它是什么类。例如,如果你有一个函数做某事:

def search(obj):
    if isinstance(obj, Mailbox):
        ...  # code to search a mailbox
    elif isinstance(obj, Document):
        ...  # code to search a document
    elif ...

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

class Mailbox:
    def search(self):
        ...  # code to search a mailbox

class Document:
    def search(self):
        ...  # code to search a document

obj.search()

What is delegation?

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

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

class UpperOut:

    def __init__(self, outfile):
        self._outfile = outfile

    def write(self, s):
        self._outfile.write(s.upper())

    def __getattr__(self, name):
        return getattr(self._outfile, name)

这里,UpperOut类重新定义write()方法,在调用底层的self.__outfile.write()方法之前将参数字符串转换为大写。所有其他方法都委托给底层的self.__outfile对象。委托通过__getattr__方法完成;有关控制属性访问的更多信息,请参阅the language reference

注意,对于更一般的情况,委派可能会变得棘手。当属性必须设置以及检索时,类必须定义一个__setattr__()方法,并且必须仔细这样做。__setattr__()的基本实现大致相当于以下内容:

class X:
    ...
    def __setattr__(self, name, value):
        self.__dict__[name] = value
    ...

大多数__setattr__()实现必须修改self.__dict__来存储本地状态,而不会导致无限递归。

How do I call a method defined in a base class from a derived class that overrides it?

使用内建super()函数:

class Derived(Base):
    def meth(self):
        super(Derived, self).meth()

对于3.0之前的版本,可以使用经典类:对于类定义,如 Derived(Base): ... 可以将Base(或Base的基类之一)中定义的方法meth()调用为 Base.meth(self, arguments ...)这里,Base.meth是一个未绑定的方法,因此您需要提供self参数。

How can I organize my code to make it easier to change the base class?

您可以为基类定义别名,在类定义之前将真正的基类分配给它,并在类中使用别名。然后,您必须更改的是分配给别名的值。顺便说一句,如果你想动态决定这个伎俩也很方便。取决于资源的可用性)使用哪个基类。例:

BaseAlias = <real base class>

class Derived(BaseAlias):
    def meth(self):
        BaseAlias.meth(self)
        ...

How do I create static class data and static class methods?

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

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

class C:
    count = 0   # number of times C.__init__ called

    def __init__(self):
        C.count = C.count + 1

    def getcount(self):
        return C.count  # or return self.count

c.count还针对任何c指代C.count,使得isinstance(c,除非由c本身或由基类搜索路径上的某个类从c.__class__返回到C C

注意:在C的方法中,像self.count = 42的赋值创建一个新的不相关的实例在self的自己的dict中命名为“count”。类静态数据名的重新绑定必须始终指定类是否在方法内:

C.count = 314

静态方法是可能的:

class C:
    @staticmethod
    def static(arg1, arg2, arg3):
        # No 'self' parameter!
        ...

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

def getcount():
    return C.count

如果您的代码被构造为每个模块定义一个类(或紧密相关的类层次结构),则提供所需的封装。

How can I overload constructors (or methods) in Python?

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

在C ++你会写

class C {
    C() { cout << "No arguments\n"; }
    C(int i) { cout << "Argument is " << i << "\n"; }
}

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

class C:
    def __init__(self, i=None):
        if i is None:
            print("No arguments")
        else:
            print("Argument is", i)

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

您还可以尝试一个变长参数列表,例如。

def __init__(self, *args):
    ...

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

I try to use __spam and I get an error about _SomeClassName__spam.

带有双引号下划线的变量名称被“修改”,以提供一种简单但有效的方法来定义类私有变量。任何形式__spam(至少两个前导下划线,最多一个尾部下划线)的标识符将以_classname__spam替换为文本,其中classname当前类名,带有任何前导下划线。

这不保证隐私:外部用户仍然可以有意访问“_classname__spam”属性,私有值在对象的__dict__中可见。许多Python程序员从不打扰使用私有变量名。

My class defines __del__ but it is not called when I delete the object.

这有几个可能的原因。

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

如果您的数据结构包含循环链接(例如一个树,其中每个孩子有一个父引用,每个父有一个孩子列表),引用计数永远不会回到零。一旦Python运行一个算法来检测这样的循环,但是垃圾回收器可能在最后一次对数据结构的引用消失后运行一段时间,因此您的__del__()方法可能会在不方便的时候调用和随机时间。如果你想重现一个问题,这是不方便的。更糟的是,对象的__del__()方法的执行顺序是任意的。您可以运行gc.collect()强制容器,但病理情况下,从不会收集对象。

尽管循环收集器,仍然是一个好主意,定义一个显式的close()方法对象被调用,当你完成他们。然后,close()方法可以删除引用subobjecs的属性。不要直接调用__del__() - __del__()应调用close()close()

避免循环引用的另一种方法是使用weakref模块,它允许您指向对象而不增加它们的引用计数。对于实例,树数据结构应使用弱引用作为其父和兄弟引用(如果他们需要它们!)。

最后,如果您的__del__()方法引发异常,则会向sys.stderr输出警告消息。

How do I get a list of all instances of a given class?

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

Why does the result of id() appear to be not unique?

id()内置函数返回一个整数,该整数在对象的生命周期内保证是唯一的。因为在CPython中,这是对象的内存地址,它频繁发生,在对象从内存中删除后,下一个新创建的对象被分配在内存中的相同位置。这通过这个例子说明:

>>> id(1000) 
13901272
>>> id(2000) 
13901272

这两个ids属于先前创建的不同整数对象,并且在执行id()调用后立即删除。要确保要检查其id的对象仍然存在,请创建对对象的另一个引用:

>>> a = 1000; b = 2000
>>> id(a) 
13901272
>>> id(b) 
13891296

Modules

How do I create a .pyc file?

When a module is imported for the first time (or when the source file has changed since the current compiled file was created) a .pyc file containing the compiled code should be created in a __pycache__ subdirectory of the directory containing the .py file. .pyc文件将以与.py文件相同的名称开头,并以.pyc结尾,中间组件,它依赖于创建它的特定python二进制文件。(有关详情,请参阅 PEP 3147)。

不能创建.pyc文件的一个原因是包含源文件的目录的权限问题,这意味着无法创建__pycache__子目录。例如,如果您作为一个用户开发,但作为另一个用户运行,例如,如果您使用Web服务器进行测试,则可能会发生这种情况。

Unless the PYTHONDONTWRITEBYTECODE environment variable is set, creation of a .pyc file is automatic if you’re importing a module and Python has the ability (permissions, free space, etc...) to create a __pycache__ subdirectory and write the compiled module to that subdirectory.

在顶级脚本上运行Python不会被视为导入,并且不会创建.pycFor example, if you have a top-level module foo.py that imports another module xyz.py, when you run foo (by typing python foo.py as a shell command), a .pyc will be created for xyz because xyz is imported, but no .pyc file will be created for foo since foo.py isn’t being imported.

如果您需要为foo创建.pyc文件 - 即为未导入的模块创建.pyc文件,您可以使用py_compilecompileall模块。

py_compile模块可以手动编译任何模块。一种方法是在该模块中以交互方式使用compile()函数:

>>> import py_compile
>>> py_compile.compile('foo.py')                 

This will write the .pyc to a __pycache__ subdirectory in the same location as foo.py (or you can override that with the optional parameter cfile).

您还可以使用compileall模块自动编译目录中的所有文件。您可以通过运行compileall.py并提供包含Python文件的目录的路径来从shell提示符进行编译:

python -m compileall .

How do I find the current module name?

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

def main():
    print('Running test...')
    ...

if __name__ == '__main__':
    main()

How can I have modules that mutually import each other?

假设您有以下模块:

foo.py:

from bar import bar_var
foo_var = 1

bar.py:

from foo import foo_var
bar_var = 2

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

  • 主要进口foo
  • 创建foo的空全局变量
  • foo被编译并开始执行
  • foo导入栏
  • 创建空的全局变量
  • bar被编译并开始执行
  • bar import foo(这是一个无操作,因为已经有一个名为foo的模块)
  • bar.foo_var = foo.foo_var

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

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

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

Guido van Rossum建议避免使用 &lt; module&gt; import ... t0>,并将所有代码放在函数中。全局变量和类变量的初始化仅应使用常量或内建函数。这意味着来自导入模块的所有内容都被引用为<module>.<name>

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

  • exports(全局变量,函数和不需要导入基类的类)
  • import语句
  • 活动代码(包括从导入值初始化的全局变量)。

van Rossum不喜欢这种方法,因为进口出现在一个奇怪的地方,但它的工作。

Matthias Urlichs建议重构您的代码,以便递归导入不是必要的。

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

__import__(‘x.y.z’) returns <module ‘x’>; how do I get z?

请考虑使用importlib中的方便函数import_module()

z = importlib.import_module('x.y.z')

When I edit an imported module and reimport it, the changes don’t show up. Why does this happen?

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

import importlib
import modname
importlib.reload(modname)

警告:这种技术不是100%防呆。特别是,包含语句的模块

from modname import some_objects

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

>>> import importlib
>>> import cls
>>> c = cls.C()                # Create an instance of C
>>> importlib.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C)       # isinstance is false?!?
False

如果你打印出类对象的“标识”,问题的性质是清楚的:

>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'