分类目录归档:知识问答

在列表中查找项目的索引

问题:在列表中查找项目的索引

给定一个列表["foo", "bar", "baz"]和列表中的项目"bar",如何1在Python中获取其索引()?

Given a list ["foo", "bar", "baz"] and an item in the list "bar", how do I get its index (1) in Python?


回答 0

>>> ["foo", "bar", "baz"].index("bar")
1

参考:数据结构>列表中的更多内容

注意事项

请注意,虽然这也许是回答这个问题最彻底的方法是问index是一个相当薄弱的组件listAPI,而我不记得我最后一次使用它的愤怒。在评论中已向我指出,由于此答案被大量引用,因此应使其更完整。有关list.index以下注意事项。最初值得一看它的文档可能是值得的:

list.index(x[, start[, end]])

在值等于x的第一项的列表中返回从零开始的索引。ValueError如果没有此类项目,则引发a 。

可选参数startend的解释与切片符号相同,用于将搜索限制到列表的特定子序列。返回的索引是相对于完整序列的开始而不是开始参数计算的。

列表长度的线性时间复杂度

一个index调用检查,以列表的每一个元素,直到它找到一个匹配。如果您的列表很长,并且您大概不知道它在列表中的什么位置,则此搜索可能会成为瓶颈。在这种情况下,您应该考虑使用其他数据结构。请注意,如果您大致知道在哪里找到匹配项,则可以给出index提示。例如,在此代码段中,l.index(999_999, 999_990, 1_000_000)它比straight快大约五个数量级l.index(999_999),因为前者只需要搜索10个条目,而后者要搜索一百万个:

>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514

仅将第一个匹配项的索引返回到其参数

呼叫index顺序搜索列表,直到找到匹配项,然后在该处停止。如果希望需要更多匹配项的索引,则应使用列表推导或生成器表达式。

>>> [1, 1].index(1)
0
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> next(g)
0
>>> next(g)
2

我曾经使用过的大多数地方index,现在我使用列表推导或生成器表达式,因为它们更具通用性。因此,如果您打算接触index,请看看这些出色的Python功能。

如果元素不在列表中则抛出

如果该项目不存在,则调用会index导致ValueError

>>> [1, 1].index(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 2 is not in list

如果该项目可能不在列表中,则您应该

  1. 首先使用item in my_list(干净,可读的方法)进行检查,或者
  2. index呼叫包裹在一个try/except可以捕获的块中ValueError(可能更快,至少在要搜索的列表较长且通常存在该项目的情况下。)
>>> ["foo", "bar", "baz"].index("bar")
1

Reference: Data Structures > More on Lists

Caveats follow

Note that while this is perhaps the cleanest way to answer the question as asked, index is a rather weak component of the list API, and I can’t remember the last time I used it in anger. It’s been pointed out to me in the comments that because this answer is heavily referenced, it should be made more complete. Some caveats about list.index follow. It is probably worth initially taking a look at the documentation for it:

list.index(x[, start[, end]])

Return zero-based index in the list of the first item whose value is equal to x. Raises a ValueError if there is no such item.

The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.

Linear time-complexity in list length

An index call checks every element of the list in order, until it finds a match. If your list is long, and you don’t know roughly where in the list it occurs, this search could become a bottleneck. In that case, you should consider a different data structure. Note that if you know roughly where to find the match, you can give index a hint. For instance, in this snippet, l.index(999_999, 999_990, 1_000_000) is roughly five orders of magnitude faster than straight l.index(999_999), because the former only has to search 10 entries, while the latter searches a million:

>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514

Only returns the index of the first match to its argument

A call to index searches through the list in order until it finds a match, and stops there. If you expect to need indices of more matches, you should use a list comprehension, or generator expression.

>>> [1, 1].index(1)
0
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> next(g)
0
>>> next(g)
2

Most places where I once would have used index, I now use a list comprehension or generator expression because they’re more generalizable. So if you’re considering reaching for index, take a look at these excellent Python features.

Throws if element not present in list

A call to index results in a ValueError if the item’s not present.

>>> [1, 1].index(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 2 is not in list

If the item might not be present in the list, you should either

  1. Check for it first with item in my_list (clean, readable approach), or
  2. Wrap the index call in a try/except block which catches ValueError (probably faster, at least when the list to search is long, and the item is usually present.)

回答 1

学习Python真正有用的一件事是使用交互式帮助功能:

>>> help(["foo", "bar", "baz"])
Help on list object:

class list(object)
 ...

 |
 |  index(...)
 |      L.index(value, [start, [stop]]) -> integer -- return first index of value
 |

这通常会引导您找到所需的方法。

One thing that is really helpful in learning Python is to use the interactive help function:

>>> help(["foo", "bar", "baz"])
Help on list object:

class list(object)
 ...

 |
 |  index(...)
 |      L.index(value, [start, [stop]]) -> integer -- return first index of value
 |

which will often lead you to the method you are looking for.


回答 2

大多数答案都说明了如何查找单个索引,但是如果该项目多次在列表中,则它们的方法不会返回多个索引。用途enumerate()

for i, j in enumerate(['foo', 'bar', 'baz']):
    if j == 'bar':
        print(i)

index()函数仅返回第一个匹配项,而enumerate()返回所有匹配项。

作为列表理解:

[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']

这也是另一个小解决方案itertools.count()(与枚举几乎相同):

from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']

对于较大的列表,这比使用enumerate()以下命令更有效:

$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop

The majority of answers explain how to find a single index, but their methods do not return multiple indexes if the item is in the list multiple times. Use enumerate():

for i, j in enumerate(['foo', 'bar', 'baz']):
    if j == 'bar':
        print(i)

The index() function only returns the first occurrence, while enumerate() returns all occurrences.

As a list comprehension:

[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']

Here’s also another small solution with itertools.count() (which is pretty much the same approach as enumerate):

from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']

This is more efficient for larger lists than using enumerate():

$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop

回答 3

要获取所有索引:

indexes = [i for i,x in enumerate(xs) if x == 'foo']

To get all indexes:

indexes = [i for i,x in enumerate(xs) if x == 'foo']

回答 4

index()返回值的第一个索引!

| 索引(…)
| L.index(value,[start,[stop]])->整数-返回值的第一个索引

def all_indices(value, qlist):
    indices = []
    idx = -1
    while True:
        try:
            idx = qlist.index(value, idx+1)
            indices.append(idx)
        except ValueError:
            break
    return indices

all_indices("foo", ["foo","bar","baz","foo"])

index() returns the first index of value!

| index(…)
| L.index(value, [start, [stop]]) -> integer — return first index of value

def all_indices(value, qlist):
    indices = []
    idx = -1
    while True:
        try:
            idx = qlist.index(value, idx+1)
            indices.append(idx)
        except ValueError:
            break
    return indices

all_indices("foo", ["foo","bar","baz","foo"])

回答 5

如果该元素不在列表中,则会出现问题。此函数处理该问题:

# if element is found it returns index of element else returns None

def find_element_in_list(element, list_element):
    try:
        index_element = list_element.index(element)
        return index_element
    except ValueError:
        return None

A problem will arise if the element is not in the list. This function handles the issue:

# if element is found it returns index of element else returns None

def find_element_in_list(element, list_element):
    try:
        index_element = list_element.index(element)
        return index_element
    except ValueError:
        return None

回答 6

a = ["foo","bar","baz",'bar','any','much']

indexes = [index for index in range(len(a)) if a[index] == 'bar']
a = ["foo","bar","baz",'bar','any','much']

indexes = [index for index in range(len(a)) if a[index] == 'bar']

回答 7

您必须设置条件以检查要搜索的元素是否在列表中

if 'your_element' in mylist:
    print mylist.index('your_element')
else:
    print None

You have to set a condition to check if the element you’re searching is in the list

if 'your_element' in mylist:
    print mylist.index('your_element')
else:
    print None

回答 8

此处提出的所有功能均会重现固有的语言行为,但会掩盖正在发生的事情。

[i for i in range(len(mylist)) if mylist[i]==myterm]  # get the indices

[each for each in mylist if each==myterm]             # get the items

mylist.index(myterm) if myterm in mylist else None    # get the first index and fail quietly

如果该语言提供了执行所需功能的方法,为什么还要编写具有异常处理功能的函数?

All of the proposed functions here reproduce inherent language behavior but obscure what’s going on.

[i for i in range(len(mylist)) if mylist[i]==myterm]  # get the indices

[each for each in mylist if each==myterm]             # get the items

mylist.index(myterm) if myterm in mylist else None    # get the first index and fail quietly

Why write a function with exception handling if the language provides the methods to do what you want itself?


回答 9

如果需要所有索引,则可以使用NumPy

import numpy as np

array = [1, 2, 1, 3, 4, 5, 1]
item = 1
np_array = np.array(array)
item_index = np.where(np_array==item)
print item_index
# Out: (array([0, 2, 6], dtype=int64),)

这是一个清晰易读的解决方案。

If you want all indexes, then you can use NumPy:

import numpy as np

array = [1, 2, 1, 3, 4, 5, 1]
item = 1
np_array = np.array(array)
item_index = np.where(np_array==item)
print item_index
# Out: (array([0, 2, 6], dtype=int64),)

It is clear, readable solution.


回答 10

在Python中给定包含该项目的列表的情况下查找项目的索引

对于列表["foo", "bar", "baz"]和列表中的项目,"bar"用Python获取索引(1)的最干净方法是什么?

好吧,可以肯定的是,这里有index方法,它返回第一次出现的索引:

>>> l = ["foo", "bar", "baz"]
>>> l.index('bar')
1

此方法存在两个问题:

  • 如果该值不在列表中,则会得到一个 ValueError
  • 如果列表中有多个值,则仅获取第一个的索引

没有值

如果该值可能丢失,则需要捕获 ValueError

您可以使用这样的可重用定义来执行此操作:

def index(a_list, value):
    try:
        return a_list.index(value)
    except ValueError:
        return None

并像这样使用它:

>>> print(index(l, 'quux'))
None
>>> print(index(l, 'bar'))
1

不利的一面是,您可能会检查返回的值isis not无:

result = index(a_list, value)
if result is not None:
    do_something(result)

列表中有多个值

如果可能发生更多次,您将无法获得有关以下方面的完整信息list.index

>>> l.append('bar')
>>> l
['foo', 'bar', 'baz', 'bar']
>>> l.index('bar')              # nothing at index 3?
1

您可以将索引枚举到列表中:

>>> [index for index, v in enumerate(l) if v == 'bar']
[1, 3]
>>> [index for index, v in enumerate(l) if v == 'boink']
[]

如果没有出现,则可以通过布尔检查结果来进行检查,或者如果对结果进行循环,则什么也不做:

indexes = [index for index, v in enumerate(l) if v == 'boink']
for index in indexes:
    do_something(index)

用熊猫更好地处理数据

如果您有熊猫,则可以通过Series对象轻松获得以下信息:

>>> import pandas as pd
>>> series = pd.Series(l)
>>> series
0    foo
1    bar
2    baz
3    bar
dtype: object

比较检查将返回一系列布尔值:

>>> series == 'bar'
0    False
1     True
2    False
3     True
dtype: bool

通过下标符号将该布尔值系列传递给该系列,您将只获得匹配的成员:

>>> series[series == 'bar']
1    bar
3    bar
dtype: object

如果只需要索引,index属性将返回一系列整数:

>>> series[series == 'bar'].index
Int64Index([1, 3], dtype='int64')

而且,如果要将它们放在列表或元组中,只需将它们传递给构造函数即可:

>>> list(series[series == 'bar'].index)
[1, 3]

是的,您也可以使用带有枚举的列表理解,但这在我看来并不那么优雅-您正在用Python进行相等性测试,而不是让用C编写的内置代码来处理它:

>>> [i for i, value in enumerate(l) if value == 'bar']
[1, 3]

这是XY问题吗?

XY问题是在询问您尝试的解决方案,而不是您的实际问题。

为什么您认为需要列表中给定元素的索引?

如果您已经知道该值,为什么还要关心它在列表中的位置?

如果值不存在,则捕获ValueError相当冗长-我宁愿避免这种情况。

无论如何,我通常都会遍历该列表,因此我通常会保留一个指向任何有趣信息的指针,并使用枚举获取索引。

如果您要处理数据,则可能应该使用pandas-与我展示的纯Python解决方法相比,pandas的工具要优雅得多。

我不记得list.index自己需要。但是,我浏览了Python标准库,并且看到了一些很好的用法。

idlelibGUI和文本解析中,有很多用途。

keyword模块使用它在模块中查找注释标记,以通过元编程自动重新生成其中的关键字列表。

在Lib / mailbox.py中,它似乎像有序映射一样在使用它:

key_list[key_list.index(old)] = new

del key_list[key_list.index(key)]

在Lib / http / cookiejar.py中,似乎用来获取下个月的内容:

mon = MONTHS_LOWER.index(mon.lower())+1

在Lib / tarfile.py中,类似于distutils来获取最多一个项目的切片:

members = members[:members.index(tarinfo)]

在Lib / pickletools.py中:

numtopop = before.index(markobject)

这些用法似乎有一个共同点,即它们似乎在受限制大小的列表上运行(由于O的n(n)查找时间而很重要list.index),并且它们主要用于解析(对于Idle,则通常用于UI)。

尽管有用例,但这种情况很少见。如果发现自己正在寻找答案,请问自己正在做的事情是否最直接地使用了该用例所用语言提供的工具。

Finding the index of an item given a list containing it in Python

For a list ["foo", "bar", "baz"] and an item in the list "bar", what’s the cleanest way to get its index (1) in Python?

Well, sure, there’s the index method, which returns the index of the first occurrence:

>>> l = ["foo", "bar", "baz"]
>>> l.index('bar')
1

There are a couple of issues with this method:

  • if the value isn’t in the list, you’ll get a ValueError
  • if more than one of the value is in the list, you only get the index for the first one

No values

If the value could be missing, you need to catch the ValueError.

You can do so with a reusable definition like this:

def index(a_list, value):
    try:
        return a_list.index(value)
    except ValueError:
        return None

And use it like this:

>>> print(index(l, 'quux'))
None
>>> print(index(l, 'bar'))
1

And the downside of this is that you will probably have a check for if the returned value is or is not None:

result = index(a_list, value)
if result is not None:
    do_something(result)

More than one value in the list

If you could have more occurrences, you’ll not get complete information with list.index:

>>> l.append('bar')
>>> l
['foo', 'bar', 'baz', 'bar']
>>> l.index('bar')              # nothing at index 3?
1

You might enumerate into a list comprehension the indexes:

>>> [index for index, v in enumerate(l) if v == 'bar']
[1, 3]
>>> [index for index, v in enumerate(l) if v == 'boink']
[]

If you have no occurrences, you can check for that with boolean check of the result, or just do nothing if you loop over the results:

indexes = [index for index, v in enumerate(l) if v == 'boink']
for index in indexes:
    do_something(index)

Better data munging with pandas

If you have pandas, you can easily get this information with a Series object:

>>> import pandas as pd
>>> series = pd.Series(l)
>>> series
0    foo
1    bar
2    baz
3    bar
dtype: object

A comparison check will return a series of booleans:

>>> series == 'bar'
0    False
1     True
2    False
3     True
dtype: bool

Pass that series of booleans to the series via subscript notation, and you get just the matching members:

>>> series[series == 'bar']
1    bar
3    bar
dtype: object

If you want just the indexes, the index attribute returns a series of integers:

>>> series[series == 'bar'].index
Int64Index([1, 3], dtype='int64')

And if you want them in a list or tuple, just pass them to the constructor:

>>> list(series[series == 'bar'].index)
[1, 3]

Yes, you could use a list comprehension with enumerate too, but that’s just not as elegant, in my opinion – you’re doing tests for equality in Python, instead of letting builtin code written in C handle it:

>>> [i for i, value in enumerate(l) if value == 'bar']
[1, 3]

Is this an XY problem?

The XY problem is asking about your attempted solution rather than your actual problem.

Why do you think you need the index given an element in a list?

If you already know the value, why do you care where it is in a list?

If the value isn’t there, catching the ValueError is rather verbose – and I prefer to avoid that.

I’m usually iterating over the list anyways, so I’ll usually keep a pointer to any interesting information, getting the index with enumerate.

If you’re munging data, you should probably be using pandas – which has far more elegant tools than the pure Python workarounds I’ve shown.

I do not recall needing list.index, myself. However, I have looked through the Python standard library, and I see some excellent uses for it.

There are many, many uses for it in idlelib, for GUI and text parsing.

The keyword module uses it to find comment markers in the module to automatically regenerate the list of keywords in it via metaprogramming.

In Lib/mailbox.py it seems to be using it like an ordered mapping:

key_list[key_list.index(old)] = new

and

del key_list[key_list.index(key)]

In Lib/http/cookiejar.py, seems to be used to get the next month:

mon = MONTHS_LOWER.index(mon.lower())+1

In Lib/tarfile.py similar to distutils to get a slice up to an item:

members = members[:members.index(tarinfo)]

In Lib/pickletools.py:

numtopop = before.index(markobject)

What these usages seem to have in common is that they seem to operate on lists of constrained sizes (important because of O(n) lookup time for list.index), and they’re mostly used in parsing (and UI in the case of Idle).

While there are use-cases for it, they are fairly uncommon. If you find yourself looking for this answer, ask yourself if what you’re doing is the most direct usage of the tools provided by the language for your use-case.


回答 11

具有该zip功能的所有索引:

get_indexes = lambda x, xs: [i for (y, i) in zip(xs, range(len(xs))) if x == y]

print get_indexes(2, [1, 2, 3, 4, 5, 6, 3, 2, 3, 2])
print get_indexes('f', 'xsfhhttytffsafweef')

All indexes with the zip function:

get_indexes = lambda x, xs: [i for (y, i) in zip(xs, range(len(xs))) if x == y]

print get_indexes(2, [1, 2, 3, 4, 5, 6, 3, 2, 3, 2])
print get_indexes('f', 'xsfhhttytffsafweef')

回答 12

获取列表中一个或多个(相同)项目的所有出现次数和位置

使用enumerate(alist)可以存储第一个元素(n),即元素x等于要查找的内容时列表的索引。

>>> alist = ['foo', 'spam', 'egg', 'foo']
>>> foo_indexes = [n for n,x in enumerate(alist) if x=='foo']
>>> foo_indexes
[0, 3]
>>>

让我们使函数findindex

该函数将项目和列表作为参数,并返回项目在列表中的位置,就像我们之前看到的那样。

def indexlist(item2find, list_or_string):
  "Returns all indexes of an item in a list or a string"
  return [n for n,item in enumerate(list_or_string) if item==item2find]

print(indexlist("1", "010101010"))

输出量


[1, 3, 5, 7]

简单

for n, i in enumerate([1, 2, 3, 4, 1]):
    if i == 1:
        print(n)

输出:

0
4

Getting all the occurrences and the position of one or more (identical) items in a list

With enumerate(alist) you can store the first element (n) that is the index of the list when the element x is equal to what you look for.

>>> alist = ['foo', 'spam', 'egg', 'foo']
>>> foo_indexes = [n for n,x in enumerate(alist) if x=='foo']
>>> foo_indexes
[0, 3]
>>>

Let’s make our function findindex

This function takes the item and the list as arguments and return the position of the item in the list, like we saw before.

def indexlist(item2find, list_or_string):
  "Returns all indexes of an item in a list or a string"
  return [n for n,item in enumerate(list_or_string) if item==item2find]

print(indexlist("1", "010101010"))

Output


[1, 3, 5, 7]

Simple

for n, i in enumerate([1, 2, 3, 4, 1]):
    if i == 1:
        print(n)

Output:

0
4

回答 13

只需您可以选择

a = [['hand', 'head'], ['phone', 'wallet'], ['lost', 'stock']]
b = ['phone', 'lost']

res = [[x[0] for x in a].index(y) for y in b]

Simply you can go with

a = [['hand', 'head'], ['phone', 'wallet'], ['lost', 'stock']]
b = ['phone', 'lost']

res = [[x[0] for x in a].index(y) for y in b]

回答 14

另外一个选项

>>> a = ['red', 'blue', 'green', 'red']
>>> b = 'red'
>>> offset = 0;
>>> indices = list()
>>> for i in range(a.count(b)):
...     indices.append(a.index(b,offset))
...     offset = indices[-1]+1
... 
>>> indices
[0, 3]
>>> 

Another option

>>> a = ['red', 'blue', 'green', 'red']
>>> b = 'red'
>>> offset = 0;
>>> indices = list()
>>> for i in range(a.count(b)):
...     indices.append(a.index(b,offset))
...     offset = indices[-1]+1
... 
>>> indices
[0, 3]
>>> 

回答 15

而现在,对于完全不同的东西…

…就像在获取索引之前确认项目的存在。这种方法的好处是,该函数始终返回一个索引列表-即使它是一个空列表。它也适用于字符串。

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    retval = []
    last = 0
    while val in l[last:]:
            i = l[last:].index(val)
            retval.append(last + i)
            last += i + 1   
    return retval

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

当粘贴到交互式python窗口中时:

Python 2.7.6 (v2.7.6:3a1db0d2747e, Nov 10 2013, 00:42:54) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(the_list, val):
...     """Always returns a list containing the indices of val in the_list"""
...     retval = []
...     last = 0
...     while val in the_list[last:]:
...             i = the_list[last:].index(val)
...             retval.append(last + i)
...             last += i + 1   
...     return retval
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

更新资料

经过一年的低沉的python开发,我对最初的答案感到有些尴尬,因此要想保持纪录,肯定可以使用上面的代码;然而,很多更地道的方式来获得相同的行为是使用列表理解,用枚举()函数一起。

像这样:

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    return [index for index, value in enumerate(l) if value == val]

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

将其粘贴到交互式python窗口中时会生成:

Python 2.7.14 |Anaconda, Inc.| (default, Dec  7 2017, 11:07:58) 
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(l, val):
...     """Always returns a list containing the indices of val in the_list"""
...     return [index for index, value in enumerate(l) if value == val]
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

现在,在回顾了这个问题和所有答案之后,我意识到这正是FMc在他先前的答案中提出的。当我最初回答这个问题时,我什至没有看到那个答案,因为我不理解。我希望我的详细示例能有助于理解。

如果上面的单行代码对您仍然没有意义,我强烈建议您使用Google“ python list comprehension”,并花一些时间来熟悉一下自己。它只是众多强大功能之一,使使用Python开发代码感到非常高兴。

And now, for something completely different…

… like confirming the existence of the item before getting the index. The nice thing about this approach is the function always returns a list of indices — even if it is an empty list. It works with strings as well.

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    retval = []
    last = 0
    while val in l[last:]:
            i = l[last:].index(val)
            retval.append(last + i)
            last += i + 1   
    return retval

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

When pasted into an interactive python window:

Python 2.7.6 (v2.7.6:3a1db0d2747e, Nov 10 2013, 00:42:54) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(the_list, val):
...     """Always returns a list containing the indices of val in the_list"""
...     retval = []
...     last = 0
...     while val in the_list[last:]:
...             i = the_list[last:].index(val)
...             retval.append(last + i)
...             last += i + 1   
...     return retval
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

Update

After another year of heads-down python development, I’m a bit embarrassed by my original answer, so to set the record straight, one can certainly use the above code; however, the much more idiomatic way to get the same behavior would be to use list comprehension, along with the enumerate() function.

Something like this:

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    return [index for index, value in enumerate(l) if value == val]

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

Which, when pasted into an interactive python window yields:

Python 2.7.14 |Anaconda, Inc.| (default, Dec  7 2017, 11:07:58) 
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(l, val):
...     """Always returns a list containing the indices of val in the_list"""
...     return [index for index, value in enumerate(l) if value == val]
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

And now, after reviewing this question and all the answers, I realize that this is exactly what FMc suggested in his earlier answer. At the time I originally answered this question, I didn’t even see that answer, because I didn’t understand it. I hope that my somewhat more verbose example will aid understanding.

If the single line of code above still doesn’t make sense to you, I highly recommend you Google ‘python list comprehension’ and take a few minutes to familiarize yourself. It’s just one of the many powerful features that make it a joy to use Python to develop code.


回答 16

FMc和user7177的答案的变体将给出一个字典,该字典可以返回任何条目的所有索引:

>>> a = ['foo','bar','baz','bar','any', 'foo', 'much']
>>> l = dict(zip(set(a), map(lambda y: [i for i,z in enumerate(a) if z is y ], set(a))))
>>> l['foo']
[0, 5]
>>> l ['much']
[6]
>>> l
{'baz': [2], 'foo': [0, 5], 'bar': [1, 3], 'any': [4], 'much': [6]}
>>> 

您也可以将其用作单个衬纸,以获取单个条目的所有索引。尽管我确实使用set(a)减少了调用lambda的次数,但是并不能保证效率。

A variant on the answer from FMc and user7177 will give a dict that can return all indices for any entry:

>>> a = ['foo','bar','baz','bar','any', 'foo', 'much']
>>> l = dict(zip(set(a), map(lambda y: [i for i,z in enumerate(a) if z is y ], set(a))))
>>> l['foo']
[0, 5]
>>> l ['much']
[6]
>>> l
{'baz': [2], 'foo': [0, 5], 'bar': [1, 3], 'any': [4], 'much': [6]}
>>> 

You could also use this as a one liner to get all indices for a single entry. There are no guarantees for efficiency, though I did use set(a) to reduce the number of times the lambda is called.


回答 17

此解决方案不如其他解决方案强大,但是如果您是初学者并且仅了解for循环,则仍然可以在避免ValueError的情况下找到项目的第一个索引:

def find_element(p,t):
    i = 0
    for e in p:
        if e == t:
            return i
        else:
            i +=1
    return -1

This solution is not as powerful as others, but if you’re a beginner and only know about forloops it’s still possible to find the first index of an item while avoiding the ValueError:

def find_element(p,t):
    i = 0
    for e in p:
        if e == t:
            return i
        else:
            i +=1
    return -1

回答 18

在列表L中查找项目x的索引:

idx = L.index(x) if (x in L) else -1

Finding index of item x in list L:

idx = L.index(x) if (x in L) else -1

回答 19

由于Python列表是从零开始的,因此我们可以使用zip内置函数,如下所示:

>>> [i for i,j in zip(range(len(haystack)), haystack) if j == 'needle' ]

其中“ haystack”是有问题的列表,“ needle”是要查找的项目。

(注意:这里我们使用i进行迭代以获取索引,但是如果我们需要专注于项目,可以切换到j。)

Since Python lists are zero-based, we can use the zip built-in function as follows:

>>> [i for i,j in zip(range(len(haystack)), haystack) if j == 'needle' ]

where “haystack” is the list in question and “needle” is the item to look for.

(Note: Here we are iterating using i to get the indexes, but if we need rather to focus on the items we can switch to j.)


回答 20

name ="bar"
list = [["foo", 1], ["bar", 2], ["baz", 3]]
new_list=[]
for item in list:
    new_list.append(item[0])
print(new_list)
try:
    location= new_list.index(name)
except:
    location=-1
print (location)

这说明了字符串是否也不在列表中,如果字符串也不在列表中,则 location = -1

name ="bar"
list = [["foo", 1], ["bar", 2], ["baz", 3]]
new_list=[]
for item in list:
    new_list.append(item[0])
print(new_list)
try:
    location= new_list.index(name)
except:
    location=-1
print (location)

This accounts for if the string is not in the list too, if it isn’t in the list then location = -1


回答 21

index()如果找不到该项目,Python 方法将引发错误。因此,相反,您可以使其类似于indexOf()JavaScript 的功能,-1如果未找到该项目,它将返回:

try:
    index = array.index('search_keyword')
except ValueError:
    index = -1

Python index() method throws an error if the item was not found. So instead you can make it similar to the indexOf() function of JavaScript which returns -1 if the item was not found:

try:
    index = array.index('search_keyword')
except ValueError:
    index = -1

回答 22

有一个更实用的答案。

list(filter(lambda x: x[1]=="bar",enumerate(["foo", "bar", "baz", "bar", "baz", "bar", "a", "b", "c"])))

更通用的形式:

def get_index_of(lst, element):
    return list(map(lambda x: x[0],\
       (list(filter(lambda x: x[1]==element, enumerate(lst))))))

There is a more functional answer to this.

list(filter(lambda x: x[1]=="bar",enumerate(["foo", "bar", "baz", "bar", "baz", "bar", "a", "b", "c"])))

More generic form:

def get_index_of(lst, element):
    return list(map(lambda x: x[0],\
       (list(filter(lambda x: x[1]==element, enumerate(lst))))))

回答 23

让我们将名称指定lst给您拥有的列表。可以将列表转换lstnumpy array。并且,然后使用numpy.where获取列表中所选项目的索引。以下是实现它的方法。

import numpy as np

lst = ["foo", "bar", "baz"]  #lst: : 'list' data type
print np.where( np.array(lst) == 'bar')[0][0]

>>> 1

Let’s give the name lst to the list that you have. One can convert the list lst to a numpy array. And, then use numpy.where to get the index of the chosen item in the list. Following is the way in which you will implement it.

import numpy as np

lst = ["foo", "bar", "baz"]  #lst: : 'list' data type
print np.where( np.array(lst) == 'bar')[0][0]

>>> 1

回答 24

对于那些来自像我这样的另一种语言的人,也许有一个简单的循环,它更易于理解和使用:

mylist = ["foo", "bar", "baz", "bar"]
newlist = enumerate(mylist)
for index, item in newlist:
  if item == "bar":
    print(index, item)

我很感激枚举到底是做什么的?。那帮助我理解了。

For those coming from another language like me, maybe with a simple loop it’s easier to understand and use it:

mylist = ["foo", "bar", "baz", "bar"]
newlist = enumerate(mylist)
for index, item in newlist:
  if item == "bar":
    print(index, item)

I am thankful for So what exactly does enumerate do?. That helped me to understand.


回答 25

如果您打算一次查找索引,则可以使用“索引”方法。但是,如果要多次搜索数据,则建议使用bisect模块。请记住,使用bisect模块的数据必须进行排序。因此,您可以对数据进行一次排序,然后可以使用二等分。在我的机器上使用bisect模块比使用索引方法快20倍。

这是使用Python 3.8及更高版本语法的代码示例:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      index 
      if (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value else -1
    )

data = list(range(1000))
# value to search
value = 666

# times to test
ttt = 1000

t1 = timeit(lambda: data.index(value), number=ttt)
t2 = timeit(lambda: bisect_search(data, value), number=ttt)

print(f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

输出:

t1=0.0400, t2=0.0020, diffs t1/t2=19.60

If you are going to find an index once then using “index” method is fine. However, if you are going to search your data more than once then I recommend using bisect module. Keep in mind that using bisect module data must be sorted. So you sort data once and then you can use bisect. Using bisect module on my machine is about 20 times faster than using index method.

Here is an example of code using Python 3.8 and above syntax:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      index 
      if (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value else -1
    )

data = list(range(1000))
# value to search
value = 666

# times to test
ttt = 1000

t1 = timeit(lambda: data.index(value), number=ttt)
t2 = timeit(lambda: bisect_search(data, value), number=ttt)

print(f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

Output:

t1=0.0400, t2=0.0020, diffs t1/t2=19.60

回答 26

如果性能值得关注:

在众多答案中提到,内置方法 list.index(item)方法是O(n)算法。如果您需要执行一次,那就很好。但是,如果您需要多次访问元素的索引,则首先创建一个由项-索引对组成的字典(O(n)),然后每次需要时在O(1)处访问索引就更有意义了。它。

如果您确定列表中的项目不会重复,则可以轻松地进行以下操作:

myList = ["foo", "bar", "baz"]

# Create the dictionary
myDict = dict((e,i) for i,e in enumerate(myList))

# Lookup
myDict["bar"] # Returns 1
# myDict.get("blah") if you don't want an error to be raised if element not found.

如果您可能有重复的元素,并且需要返回其所有索引:

from collections import defaultdict as dd
myList = ["foo", "bar", "bar", "baz", "foo"]

# Create the dictionary
myDict = dd(list)
for i,e in enumerate(myList):
    myDict[e].append(i)

# Lookup
myDict["foo"] # Returns [0, 4]

If performance is of concern:

It is mentioned in numerous answers that the built-in method of list.index(item) method is an O(n) algorithm. It is fine if you need to perform this once. But if you need to access the indices of elements a number of times, it makes more sense to first create a dictionary (O(n)) of item-index pairs, and then access the index at O(1) every time you need it.

If you are sure that the items in your list are never repeated, you can easily:

myList = ["foo", "bar", "baz"]

# Create the dictionary
myDict = dict((e,i) for i,e in enumerate(myList))

# Lookup
myDict["bar"] # Returns 1
# myDict.get("blah") if you don't want an error to be raised if element not found.

If you may have duplicate elements, and need to return all of their indices:

from collections import defaultdict as dd
myList = ["foo", "bar", "bar", "baz", "foo"]

# Create the dictionary
myDict = dd(list)
for i,e in enumerate(myList):
    myDict[e].append(i)

# Lookup
myDict["foo"] # Returns [0, 4]

回答 27

如@TerryA所示,许多答案都讨论了如何查找一个索引。

more_itertools是一个第三方库,具有用于在可迭代对象中定位多个索引的工具。

给定

import more_itertools as mit


iterable = ["foo", "bar", "baz", "ham", "foo", "bar", "baz"]

查找多个观测值的索引:

list(mit.locate(iterable, lambda x: x == "bar"))
# [1, 5]

测试多个项目:

list(mit.locate(iterable, lambda x: x in {"bar", "ham"}))
# [1, 3, 5]

另请参见使用的更多选项more_itertools.locate。通过安装> pip install more_itertools

As indicated by @TerryA, many answers discuss how to find one index.

more_itertools is a third-party library with tools to locate multiple indices within an iterable.

Given

import more_itertools as mit


iterable = ["foo", "bar", "baz", "ham", "foo", "bar", "baz"]

Code

Find indices of multiple observations:

list(mit.locate(iterable, lambda x: x == "bar"))
# [1, 5]

Test multiple items:

list(mit.locate(iterable, lambda x: x in {"bar", "ham"}))
# [1, 3, 5]

See also more options with more_itertools.locate. Install via > pip install more_itertools.


回答 28

使用dictionary,其中首先处理列表,然后向其添加索引

from collections import defaultdict

index_dict = defaultdict(list)    
word_list =  ['foo','bar','baz','bar','any', 'foo', 'much']

for word_index in range(len(word_list)) :
    index_dict[word_list[word_index]].append(word_index)

word_index_to_find = 'foo'       
print(index_dict[word_index_to_find])

# output :  [0, 5]

using dictionary , where process the list first and then add the index to it

from collections import defaultdict

index_dict = defaultdict(list)    
word_list =  ['foo','bar','baz','bar','any', 'foo', 'much']

for word_index in range(len(word_list)) :
    index_dict[word_list[word_index]].append(word_index)

word_index_to_find = 'foo'       
print(index_dict[word_index_to_find])

# output :  [0, 5]

回答 29

在我看来,这["foo", "bar", "baz"].index("bar")是好的,但还不够!因为如果“ bar”不在字典中,请ValueError提出。因此,您可以使用以下功能:

def find_index(arr, name):
    try:
        return arr.index(name)
    except ValueError:
        return -1

if __name__ == '__main__':
    print(find_index(["foo", "bar", "baz"], "bar"))

结果是:

1个

如果name不是arr,则函数返回-1。例如:

打印(find_index([“ foo”,“ bar”,“ baz”],“ fooo”))

-1

in my opinion the ["foo", "bar", "baz"].index("bar") is good but it isn’t enough!because if “bar” isn’t in dictionary,ValueError raised.So you can use this function:

def find_index(arr, name):
    try:
        return arr.index(name)
    except ValueError:
        return -1

if __name__ == '__main__':
    print(find_index(["foo", "bar", "baz"], "bar"))

and the result is:

1

and if name wasn’t at arr,the function return -1.for example:

print(find_index([“foo”, “bar”, “baz”], “fooo”))

-1


使用“ for”循环遍历字典

问题:使用“ for”循环遍历字典

以下代码使我有些困惑:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print key, 'corresponds to', d[key]

我不明白的是那key部分。Python如何识别它仅需要从字典中读取密钥?是keyPython中的特殊字?还是仅仅是一个变量?

I am a bit puzzled by the following code:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print key, 'corresponds to', d[key]

What I don’t understand is the key portion. How does Python recognize that it needs only to read the key from the dictionary? Is key a special word in Python? Or is it simply a variable?


回答 0

key 只是一个变量名。

for key in d:

只会循环遍历字典中的键,而不是键和值。要遍历键和值,可以使用以下命令:

对于Python 3.x:

for key, value in d.items():

对于Python 2.x:

for key, value in d.iteritems():

要测试自己,请将单词更改keypoop

在Python 3.x中,iteritems()替换为simple items(),它返回由dict支持的类似set的视图,iteritems()但效果更好。在2.7中也可用viewitems()

该操作items()将对2和3都适用,但是在2中,它将返回字典(key, value)对的列表,该列表将不反映items()调用后发生的字典更改。如果要在3.x中使用2.x行为,可以调用list(d.items())

key is just a variable name.

for key in d:

will simply loop over the keys in the dictionary, rather than the keys and values. To loop over both key and value you can use the following:

For Python 3.x:

for key, value in d.items():

For Python 2.x:

for key, value in d.iteritems():

To test for yourself, change the word key to poop.

In Python 3.x, iteritems() was replaced with simply items(), which returns a set-like view backed by the dict, like iteritems() but even better. This is also available in 2.7 as viewitems().

The operation items() will work for both 2 and 3, but in 2 it will return a list of the dictionary’s (key, value) pairs, which will not reflect changes to the dict that happen after the items() call. If you want the 2.x behavior in 3.x, you can call list(d.items()).


回答 1

并不是说键是一个特殊的词,而是字典实现了迭代器协议。您可以在您的类中执行此操作,例如,有关如何构建类迭代器的信息,请参见此问题

对于字典,它是在C级别实现的。详细信息在PEP 234中可用。特别是标题为“字典迭代器”的部分:

  • 字典实现了一个tp_iter插槽,该插槽返回一个有效的迭代器,该迭代器对字典的键进行迭代。[…]这意味着我们可以写

    for k in dict: ...

    等同于,但是比

    for k in dict.keys(): ...

    只要不违反对字典修改的限制(无论是通过循环还是通过另一个线程)。

  • 将方法添加到字典中,以显式返回不同种类的迭代器:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...

    for x in dict是的简写for x in dict.iterkeys()

在Python 3中dict.iterkeys()dict.itervalues()dict.iteritems()不再受支持。使用dict.keys()dict.values()dict.items()代替。

It’s not that key is a special word, but that dictionaries implement the iterator protocol. You could do this in your class, e.g. see this question for how to build class iterators.

In the case of dictionaries, it’s implemented at the C level. The details are available in PEP 234. In particular, the section titled “Dictionary Iterators”:

  • Dictionaries implement a tp_iter slot that returns an efficient iterator that iterates over the keys of the dictionary. […] This means that we can write

    for k in dict: ...
    

    which is equivalent to, but much faster than

    for k in dict.keys(): ...
    

    as long as the restriction on modifications to the dictionary (either by the loop or by another thread) are not violated.

  • Add methods to dictionaries that return different kinds of iterators explicitly:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...
    

    This means that for x in dict is shorthand for for x in dict.iterkeys().

In Python 3, dict.iterkeys(), dict.itervalues() and dict.iteritems() are no longer supported. Use dict.keys(), dict.values() and dict.items() instead.


回答 2

遍历一个dict通过其按键迭代没有特定的顺序,你可以在这里看到:

编辑:(Python3.6中不再是这种情况,但是请注意,尚不能保证它的行为)

>>> d = {'x': 1, 'y': 2, 'z': 3} 
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

对于您的示例,最好使用dict.items()

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

这给您一个元组列表。当你遍历他们这个样子,每个元组是解压到kv自动:

for k,v in d.items():
    print(k, 'corresponds to', v)

如果循环的主体只有几行,则在遍历a时使用kv作为变量名dict非常普遍。对于更复杂的循环,最好使用更具描述性的名称:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

养成使用格式字符串的习惯是一个好主意:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))

Iterating over a dict iterates through its keys in no particular order, as you can see here:

Edit: (This is no longer the case in Python3.6, but note that it’s not guaranteed behaviour yet)

>>> d = {'x': 1, 'y': 2, 'z': 3} 
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

For your example, it is a better idea to use dict.items():

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

This gives you a list of tuples. When you loop over them like this, each tuple is unpacked into k and v automatically:

for k,v in d.items():
    print(k, 'corresponds to', v)

Using k and v as variable names when looping over a dict is quite common if the body of the loop is only a few lines. For more complicated loops it may be a good idea to use more descriptive names:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

It’s a good idea to get into the habit of using format strings:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))

回答 3

key 只是一个变量。

对于Python2.X

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

… 或更好,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

对于Python3.X

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)

key is simply a variable.

For Python2.X:

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

… or better,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

For Python3.X:

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)

回答 4

当您使用for .. in ..-syntax 遍历字典时,它总是在键上进行遍历(使用可以访问值dictionary[key])。

要遍历键值对,请在Python 2中使用for k,v in s.iteritems(),在Python 3中for k,v in s.items()

When you iterate through dictionaries using the for .. in ..-syntax, it always iterates over the keys (the values are accessible using dictionary[key]).

To iterate over key-value pairs, in Python 2 use for k,v in s.iteritems(), and in Python 3 for k,v in s.items().


回答 5

这是一个非常常见的循环习惯用法。in是运算符。有关何时使用for key in dict和何时使用的信息,for key in dict.keys()请参阅David Goodger的Idiomatic Python文章(归档副本)

This is a very common looping idiom. in is an operator. For when to use for key in dict and when it must be for key in dict.keys() see David Goodger’s Idiomatic Python article (archived copy).


回答 6

使用“ for”循环遍历字典

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

Python如何识别它仅需要从字典中读取密钥?关键字在Python中是一个特殊的词吗?还是仅仅是一个变量?

不只是for循环。这里重要的词是“迭代”。

字典是键到值的映射:

d = {'x': 1, 'y': 2, 'z': 3} 

每当我们遍历它时,我们都会遍历键。变量名key仅是描述性的,非常适合此目的。

这发生在列表理解中:

>>> [k for k in d]
['x', 'y', 'z']

当我们将字典传递到列表(或任何其他集合类型对象)时,就会发生这种情况:

>>> list(d)
['x', 'y', 'z']

Python迭代的方式是在需要的上下文中调用__iter__对象的方法(在这种情况下为字典),该方法返回迭代器(在这种情况下为keyiterator对象):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

我们不应该自己使用这些特殊方法,而是使用各自的内置函数来调用它iter

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

迭代器有一个__next__方法-但我们使用内置函数来调用它next

>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

当迭代器用尽时,它将引发StopIteration。这就是Python知道退出for循环,列表理解,生成器表达式或任何其他迭代上下文的方式。迭代器一旦引发,StopIteration它就会一直引发-如果您想再次进行迭代,则需要一个新的迭代器。

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

返回字典

我们已经看到在许多情况下都会反复进行命令。我们看到的是,每当我们迭代一个字典时,我们都会得到密钥。回到原始示例:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

如果我们更改变量名,我们仍然会得到键。让我们尝试一下:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

如果要遍历值,则需要使用.valuesdicts方法,或同时使用dicts方法.items

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

在给定的示例中,迭代如下所示的项将更加有效:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

但是出于学术目的,这个问题的例子很好。

Iterating over dictionaries using ‘for’ loops

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

How does Python recognize that it needs only to read the key from the dictionary? Is key a special word in Python? Or is it simply a variable?

It’s not just for loops. The important word here is “iterating”.

A dictionary is a mapping of keys to values:

d = {'x': 1, 'y': 2, 'z': 3} 

Any time we iterate over it, we iterate over the keys. The variable name key is only intended to be descriptive – and it is quite apt for the purpose.

This happens in a list comprehension:

>>> [k for k in d]
['x', 'y', 'z']

It happens when we pass the dictionary to list (or any other collection type object):

>>> list(d)
['x', 'y', 'z']

The way Python iterates is, in a context where it needs to, it calls the __iter__ method of the object (in this case the dictionary) which returns an iterator (in this case, a keyiterator object):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

We shouldn’t use these special methods ourselves, instead, use the respective builtin function to call it, iter:

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

Iterators have a __next__ method – but we call it with the builtin function, next:

>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

When an iterator is exhausted, it raises StopIteration. This is how Python knows to exit a for loop, or a list comprehension, or a generator expression, or any other iterative context. Once an iterator raises StopIteration it will always raise it – if you want to iterate again, you need a new one.

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

Returning to dicts

We’ve seen dicts iterating in many contexts. What we’ve seen is that any time we iterate over a dict, we get the keys. Back to the original example:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

If we change the variable name, we still get the keys. Let’s try it:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

If we want to iterate over the values, we need to use the .values method of dicts, or for both together, .items:

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

In the example given, it would be more efficient to iterate over the items like this:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

But for academic purposes, the question’s example is just fine.


回答 7

我有一个用例,我必须遍历字典以获取键,值对以及指示我在哪里的索引。这是我的方法:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

请注意,键值周围的括号很重要,如果没有括号,则会出现ValueError“没有足够的值要解压”。

I have a use case where I have to iterate through the dict to get the key, value pair, also the index indicating where I am. This is how I do it:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

Note that the parentheses around the key, value is important, without the parentheses, you get an ValueError “not enough values to unpack”.


回答 8

您可以dicttype在GitHub上检查CPython的实现。这是实现dict迭代器的方法的签名:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython的dictobject.c

You can check the implementation of CPython’s dicttype on GitHub. This is the signature of method that implements the dict iterator:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython dictobject.c


回答 9

要遍历键,使用起来比较慢,但效果更好my_dict.keys()。如果您尝试执行以下操作:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

这将导致运行时错误,因为在程序运行时更改了密钥。如果您绝对希望减少时间,请使用此for key in my_dict方法,但已被警告;)。

To iterate over keys, it is slower but better to use my_dict.keys(). If you tried to do something like this:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

it would create a runtime error because you are changing the keys while the program is running. If you are absolutely set on reducing time, use the for key in my_dict way, but you have been warned ;).


回答 10

这将按照值的升序打印输出。

d = {'x': 3, 'y': 1, 'z': 2}
def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

输出:

在此处输入图片说明

This will print the output in Sorted order by Values in ascending order.

d = {'x': 3, 'y': 1, 'z': 2}
def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

Output:

enter image description here


Python的list方法append和extend有什么区别?

问题:Python的list方法append和extend有什么区别?

列表方法append()和之间有什么区别extend()

What’s the difference between the list methods append() and extend()?


回答 0

append:在末尾追加对象。

x = [1, 2, 3]
x.append([4, 5])
print (x)

给你: [1, 2, 3, [4, 5]]


extend:通过附加来自iterable的元素来扩展列表。

x = [1, 2, 3]
x.extend([4, 5])
print (x)

给你: [1, 2, 3, 4, 5]

append: Appends object at the end.

x = [1, 2, 3]
x.append([4, 5])
print (x)

gives you: [1, 2, 3, [4, 5]]


extend: Extends list by appending elements from the iterable.

x = [1, 2, 3]
x.extend([4, 5])
print (x)

gives you: [1, 2, 3, 4, 5]


回答 1

append将元素添加到列表,并将extend第一个列表与另一个列表(或另一个可迭代的列表,不一定是列表)连接。

>>> li = ['a', 'b', 'mpilgrim', 'z', 'example']
>>> li
['a', 'b', 'mpilgrim', 'z', 'example']

>>> li.append("new")
>>> li
['a', 'b', 'mpilgrim', 'z', 'example', 'new']

>>> li.append(["new", 2])
>>> li
['a', 'b', 'mpilgrim', 'z', 'example', 'new', ['new', 2]]

>>> li.insert(2, "new")
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', ['new', 2]]

>>> li.extend(["two", "elements"])
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', ['new', 2], 'two', 'elements']

append adds an element to a list, and extend concatenates the first list with another list (or another iterable, not necessarily a list.)

>>> li = ['a', 'b', 'mpilgrim', 'z', 'example']
>>> li
['a', 'b', 'mpilgrim', 'z', 'example']

>>> li.append("new")
>>> li
['a', 'b', 'mpilgrim', 'z', 'example', 'new']

>>> li.append(["new", 2])
>>> li
['a', 'b', 'mpilgrim', 'z', 'example', 'new', ['new', 2]]

>>> li.insert(2, "new")
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', ['new', 2]]

>>> li.extend(["two", "elements"])
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', ['new', 2], 'two', 'elements']

回答 2

列表方法追加和扩展之间有什么区别?

  • append将其参数作为单个元素添加到列表的末尾。列表本身的长度将增加一。
  • extend遍历其参数,将每个元素添加到列表,扩展列表。无论迭代参数中有多少元素,列表的长度都会增加。

append

list.append方法将一个对象附加到列表的末尾。

my_list.append(object) 

无论对象是什么,无论是数字,字符串,另一个列表还是其他对象,它都将my_list作为单个条目添加到列表的末尾。

>>> my_list
['foo', 'bar']
>>> my_list.append('baz')
>>> my_list
['foo', 'bar', 'baz']

因此请记住,列表是一个对象。如果将另一个列表追加到列表中,则第一个列表将是列表末尾的单个对象(可能不是您想要的):

>>> another_list = [1, 2, 3]
>>> my_list.append(another_list)
>>> my_list
['foo', 'bar', 'baz', [1, 2, 3]]
                     #^^^^^^^^^--- single item at the end of the list.

extend

list.extend方法通过附加来自可迭代对象的元素来扩展列表:

my_list.extend(iterable)

因此,通过扩展,可迭代的每个元素都将附加到列表中。例如:

>>> my_list
['foo', 'bar']
>>> another_list = [1, 2, 3]
>>> my_list.extend(another_list)
>>> my_list
['foo', 'bar', 1, 2, 3]

请记住,字符串是可迭代的,因此,如果用字符串扩展列表,则在迭代字符串时将附加每个字符(可能不是您想要的):

>>> my_list.extend('baz')
>>> my_list
['foo', 'bar', 1, 2, 3, 'b', 'a', 'z']

运算符重载,__add__+)和__iadd__+=

这两个++=运营商的定义list。它们在语义上类似扩展。

my_list + another_list 在内存中创建第三个列表,因此您可以返回它的结果,但是它要求第二个可迭代的列表。

my_list += another_list就地修改列表(如我们所见,它就地运算符,并且列表是可变对象),因此不会创建新列表。它也像扩展一样工作,因为第二个可迭代对象可以是任何一种可迭代对象。

不要混淆- my_list = my_list + another_list不等于+=-它为您提供了分配给my_list的全新列表。

时间复杂度

追加具有恒定的时间复杂度 O(1)。

扩展具有时间复杂度O(k)。

遍历多次调用会append增加复杂性,使其等效于extend的复杂性,并且由于extend的迭代是在C中实现的,因此,如果您打算将可迭代对象的后续项追加到列表中,它将总是更快。

性能

您可能会想知道什么是性能更高的,因为append可以用来实现与extend相同的结果。以下功能执行相同的操作:

def append(alist, iterable):
    for item in iterable:
        alist.append(item)

def extend(alist, iterable):
    alist.extend(iterable)

因此,让我们为它们计时:

import timeit

>>> min(timeit.repeat(lambda: append([], "abcdefghijklmnopqrstuvwxyz")))
2.867846965789795
>>> min(timeit.repeat(lambda: extend([], "abcdefghijklmnopqrstuvwxyz")))
0.8060121536254883

在时间上发表评论

评论者说:

完美的答案,我只是错过了仅添加一个元素进行比较的时机

做语义上正确的事情。如果您想将所有元素附加到可迭代对象中,请使用extend。如果您仅添加一个元素,请使用append

好的,让我们创建一个实验来看看如何及时进行:

def append_one(a_list, element):
    a_list.append(element)

def extend_one(a_list, element):
    """creating a new list is semantically the most direct
    way to create an iterable to give to extend"""
    a_list.extend([element])

import timeit

而且我们看到,单单使用扩展创建一个可迭代的方法是(少量)浪费时间:

>>> min(timeit.repeat(lambda: append_one([], 0)))
0.2082819009956438
>>> min(timeit.repeat(lambda: extend_one([], 0)))
0.2397019260097295

我们从中了解到,extend只有一个元素要附加时,使用并没有任何好处。

同样,这些时间并不是那么重要。我只是向他们说明,在Python中做正确的语义就是正确的方法。

可以想象,您可以在两个可比较的操作上测试时序,并得到模棱两可或相反的结果。只要专注于做语义上正确的事情。

结论

我们看到,extend在语义上更清晰,而且它可以比运行速度非常快append当你打算在一个迭代的每个元素添加到列表中。

如果只有一个元素(不可迭代)添加到列表中,请使用append

What is the difference between the list methods append and extend?

  • append adds its argument as a single element to the end of a list. The length of the list itself will increase by one.
  • extend iterates over its argument adding each element to the list, extending the list. The length of the list will increase by however many elements were in the iterable argument.

append

The list.append method appends an object to the end of the list.

my_list.append(object) 

Whatever the object is, whether a number, a string, another list, or something else, it gets added onto the end of my_list as a single entry on the list.

>>> my_list
['foo', 'bar']
>>> my_list.append('baz')
>>> my_list
['foo', 'bar', 'baz']

So keep in mind that a list is an object. If you append another list onto a list, the first list will be a single object at the end of the list (which may not be what you want):

>>> another_list = [1, 2, 3]
>>> my_list.append(another_list)
>>> my_list
['foo', 'bar', 'baz', [1, 2, 3]]
                     #^^^^^^^^^--- single item at the end of the list.

extend

The list.extend method extends a list by appending elements from an iterable:

my_list.extend(iterable)

So with extend, each element of the iterable gets appended onto the list. For example:

>>> my_list
['foo', 'bar']
>>> another_list = [1, 2, 3]
>>> my_list.extend(another_list)
>>> my_list
['foo', 'bar', 1, 2, 3]

Keep in mind that a string is an iterable, so if you extend a list with a string, you’ll append each character as you iterate over the string (which may not be what you want):

>>> my_list.extend('baz')
>>> my_list
['foo', 'bar', 1, 2, 3, 'b', 'a', 'z']

Operator Overload, __add__ (+) and __iadd__ (+=)

Both + and += operators are defined for list. They are semantically similar to extend.

my_list + another_list creates a third list in memory, so you can return the result of it, but it requires that the second iterable be a list.

my_list += another_list modifies the list in-place (it is the in-place operator, and lists are mutable objects, as we’ve seen) so it does not create a new list. It also works like extend, in that the second iterable can be any kind of iterable.

Don’t get confused – my_list = my_list + another_list is not equivalent to += – it gives you a brand new list assigned to my_list.

Time Complexity

Append has constant time complexity, O(1).

Extend has time complexity, O(k).

Iterating through the multiple calls to append adds to the complexity, making it equivalent to that of extend, and since extend’s iteration is implemented in C, it will always be faster if you intend to append successive items from an iterable onto a list.

Performance

You may wonder what is more performant, since append can be used to achieve the same outcome as extend. The following functions do the same thing:

def append(alist, iterable):
    for item in iterable:
        alist.append(item)

def extend(alist, iterable):
    alist.extend(iterable)

So let’s time them:

import timeit

>>> min(timeit.repeat(lambda: append([], "abcdefghijklmnopqrstuvwxyz")))
2.867846965789795
>>> min(timeit.repeat(lambda: extend([], "abcdefghijklmnopqrstuvwxyz")))
0.8060121536254883

Addressing a comment on timings

A commenter said:

Perfect answer, I just miss the timing of comparing adding only one element

Do the semantically correct thing. If you want to append all elements in an iterable, use extend. If you’re just adding one element, use append.

Ok, so let’s create an experiment to see how this works out in time:

def append_one(a_list, element):
    a_list.append(element)

def extend_one(a_list, element):
    """creating a new list is semantically the most direct
    way to create an iterable to give to extend"""
    a_list.extend([element])

import timeit

And we see that going out of our way to create an iterable just to use extend is a (minor) waste of time:

>>> min(timeit.repeat(lambda: append_one([], 0)))
0.2082819009956438
>>> min(timeit.repeat(lambda: extend_one([], 0)))
0.2397019260097295

We learn from this that there’s nothing gained from using extend when we have only one element to append.

Also, these timings are not that important. I am just showing them to make the point that, in Python, doing the semantically correct thing is doing things the Right Way™.

It’s conceivable that you might test timings on two comparable operations and get an ambiguous or inverse result. Just focus on doing the semantically correct thing.

Conclusion

We see that extend is semantically clearer, and that it can run much faster than append, when you intend to append each element in an iterable to a list.

If you only have a single element (not in an iterable) to add to the list, use append.


回答 3

append追加一个元素。extend追加元素列表。

请注意,如果您传递要追加的列表,它仍会添加一个元素:

>>> a = [1, 2, 3]
>>> a.append([4, 5, 6])
>>> a
[1, 2, 3, [4, 5, 6]]

append appends a single element. extend appends a list of elements.

Note that if you pass a list to append, it still adds one element:

>>> a = [1, 2, 3]
>>> a.append([4, 5, 6])
>>> a
[1, 2, 3, [4, 5, 6]]

回答 4

追加与扩充

在此处输入图片说明

使用append,您可以附加一个元素来扩展列表:

>>> a = [1,2]
>>> a.append(3)
>>> a
[1,2,3]

如果要扩展多个元素,则应使用extend,因为您只能附加一个元素或一个元素列表:

>>> a.append([4,5])
>>> a
>>> [1,2,3,[4,5]]

这样您就可以获得一个嵌套列表

您可以像这样通过扩展来扩展单个元素

>>> a = [1,2]
>>> a.extend([3])
>>> a
[1,2,3]

或者,与追加不同的是,一次扩展更多元素而不将列表嵌套到原始列表中(这就是名称扩展的原因)

>>> a.extend([4,5,6])
>>> a
[1,2,3,4,5,6]

两种方法都添加一个元素

在此处输入图片说明

尽管添加和添加都比较简单,但是添加和扩展都可以在列表末尾添加一个元素。

追加1个元素

>>> x = [1,2]
>>> x.append(3)
>>> x
[1,2,3]

扩展一个元素

>>> x = [1,2]
>>> x.extend([3])
>>> x
[1,2,3]

添加更多元素…结果不同

如果对多个元素使用append,则必须将元素列表作为参数传递,您将获得NESTED列表!

>>> x = [1,2]
>>> x.append([3,4])
>>> x
[1,2,[3,4]]

相反,使用extend,您将一个列表作为参数传递,但是您将获得一个列表,其中包含未嵌套在旧元素中的新元素。

>>> z = [1,2] 
>>> z.extend([3,4])
>>> z
[1,2,3,4]

因此,使用更多元素,您将使用extend获得包含更多项目的列表。但是,追加列表不会在列表中添加更多元素,而是一个嵌套列表的元素,您可以在代码输出中清楚地看到。

在此处输入图片说明

在此处输入图片说明

Append vs Extend

enter image description here

With append you can append a single element that will extend the list:

>>> a = [1,2]
>>> a.append(3)
>>> a
[1,2,3]

If you want to extend more than one element you should use extend, because you can only append one elment or one list of element:

>>> a.append([4,5])
>>> a
>>> [1,2,3,[4,5]]

So that you get a nested list

Instead with extend, you can extend a single element like this

>>> a = [1,2]
>>> a.extend([3])
>>> a
[1,2,3]

Or, differently, from append, extend more elements in one time without nesting the list into the original one (that’s the reason of the name extend)

>>> a.extend([4,5,6])
>>> a
[1,2,3,4,5,6]

Adding one element with both methods

enter image description here

Both append and extend can add one element to the end of the list, though append is simpler.

append 1 element

>>> x = [1,2]
>>> x.append(3)
>>> x
[1,2,3]

extend one element

>>> x = [1,2]
>>> x.extend([3])
>>> x
[1,2,3]

Adding more elements… with different results

If you use append for more than one element, you have to pass a list of elements as arguments and you will obtain a NESTED list!

>>> x = [1,2]
>>> x.append([3,4])
>>> x
[1,2,[3,4]]

With extend, instead, you pass a list as an argument, but you will obtain a list with the new element that is not nested in the old one.

>>> z = [1,2] 
>>> z.extend([3,4])
>>> z
[1,2,3,4]

So, with more elements, you will use extend to get a list with more items. However, appending a list will not add more elements to the list, but one element that is a nested list as you can clearly see in the output of the code.

enter image description here

enter image description here


回答 5

以下两个片段在语义上是等效的:

for item in iterator:
    a_list.append(item)

a_list.extend(iterator)

当循环在C中实现时,后者可能会更快。

The following two snippets are semantically equivalent:

for item in iterator:
    a_list.append(item)

and

a_list.extend(iterator)

The latter may be faster as the loop is implemented in C.


回答 6

append()方法将单个项目添加到列表的末尾。

x = [1, 2, 3]
x.append([4, 5])
x.append('abc')
print(x)
# gives you
[1, 2, 3, [4, 5], 'abc']

extend()方法采用一个参数,一个列表,并将该参数的每个项目附加到原始列表中。(列表以类的形式实现。“创建”列表实际上是在实例化一个类。因此,列表具有对其进行操作的方法。)

x = [1, 2, 3]
x.extend([4, 5])
x.extend('abc')
print(x)
# gives you
[1, 2, 3, 4, 5, 'a', 'b', 'c']

潜入Python

The append() method adds a single item to the end of the list.

x = [1, 2, 3]
x.append([4, 5])
x.append('abc')
print(x)
# gives you
[1, 2, 3, [4, 5], 'abc']

The extend() method takes one argument, a list, and appends each of the items of the argument to the original list. (Lists are implemented as classes. “Creating” a list is really instantiating a class. As such, a list has methods that operate on it.)

x = [1, 2, 3]
x.extend([4, 5])
x.extend('abc')
print(x)
# gives you
[1, 2, 3, 4, 5, 'a', 'b', 'c']

From Dive Into Python.


回答 7

您可以使用“ +”返回扩展名,而不是就地扩展名。

l1=range(10)

l1+[11]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]

l2=range(10,1,-1)

l1+l2

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2]

+=就地行为类似,但与append&略有不同extend。其中一个最大的不同+=,从appendextend是当它在功能范围时,看到这个博客帖子

You can use “+” for returning extend, instead of extending in place.

l1=range(10)

l1+[11]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]

l2=range(10,1,-1)

l1+l2

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2]

Similarly += for in place behavior, but with slight differences from append & extend. One of the biggest differences of += from append and extend is when it is used in function scopes, see this blog post.


回答 8

append(object) -通过将对象添加到列表来更新列表。

x = [20]
# List passed to the append(object) method is treated as a single object.
x.append([21, 22, 23])
# Hence the resultant list length will be 2
print(x)
--> [20, [21, 22, 23]]

extend(list) -本质上是串联两个列表。

x = [20]
# The parameter passed to extend(list) method is treated as a list.
# Eventually it is two lists being concatenated.
x.extend([21, 22, 23])
# Here the resultant list's length is 4
print(x)
[20, 21, 22, 23]

append(object) – Updates the list by adding an object to the list.

x = [20]
# List passed to the append(object) method is treated as a single object.
x.append([21, 22, 23])
# Hence the resultant list length will be 2
print(x)
--> [20, [21, 22, 23]]

extend(list) – Essentially concatenates two lists.

x = [20]
# The parameter passed to extend(list) method is treated as a list.
# Eventually it is two lists being concatenated.
x.extend([21, 22, 23])
# Here the resultant list's length is 4
print(x)
[20, 21, 22, 23]

回答 9

extend()可以与迭代器参数一起使用。这是一个例子。您希望通过以下方式从列表列表中列出一个列表:

list2d = [[1,2,3],[4,5,6], [7], [8,9]]

你要

>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

您可能itertools.chain.from_iterable()会这样做。该方法的输出是一个迭代器。它的实现等效于

def from_iterable(iterables):
    # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
    for it in iterables:
        for element in it:
            yield element

回到我们的例子,我们可以做

import itertools
list2d = [[1,2,3],[4,5,6], [7], [8,9]]
merged = list(itertools.chain.from_iterable(list2d))

并获得通缉名单。

以下是等效extend()用于迭代器参数的方法:

merged = []
merged.extend(itertools.chain.from_iterable(list2d))
print(merged)
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

extend() can be used with an iterator argument. Here is an example. You wish to make a list out of a list of lists this way:

From

list2d = [[1,2,3],[4,5,6], [7], [8,9]]

you want

>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

You may use itertools.chain.from_iterable() to do so. This method’s output is an iterator. Its implementation is equivalent to

def from_iterable(iterables):
    # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
    for it in iterables:
        for element in it:
            yield element

Back to our example, we can do

import itertools
list2d = [[1,2,3],[4,5,6], [7], [8,9]]
merged = list(itertools.chain.from_iterable(list2d))

and get the wanted list.

Here is how equivalently extend() can be used with an iterator argument:

merged = []
merged.extend(itertools.chain.from_iterable(list2d))
print(merged)
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

回答 10

这等效于appendextend使用+运算符:

>>> x = [1,2,3]
>>> x
[1, 2, 3]
>>> x = x + [4,5,6] # Extend
>>> x
[1, 2, 3, 4, 5, 6]
>>> x = x + [[7,8]] # Append
>>> x
[1, 2, 3, 4, 5, 6, [7, 8]]

This is the equivalent of append and extend using the + operator:

>>> x = [1,2,3]
>>> x
[1, 2, 3]
>>> x = x + [4,5,6] # Extend
>>> x
[1, 2, 3, 4, 5, 6]
>>> x = x + [[7,8]] # Append
>>> x
[1, 2, 3, 4, 5, 6, [7, 8]]

回答 11

append():基本上在Python中用于添加一个元素。

范例1:

>> a = [1, 2, 3, 4]
>> a.append(5)
>> print(a)
>> a = [1, 2, 3, 4, 5]

范例2:

>> a = [1, 2, 3, 4]
>> a.append([5, 6])
>> print(a)
>> a = [1, 2, 3, 4, [5, 6]]

extend():extend()用于合并两个列表或在一个列表中插入多个元素。

范例1:

>> a = [1, 2, 3, 4]
>> b = [5, 6, 7, 8]
>> a.extend(b)
>> print(a)
>> a = [1, 2, 3, 4, 5, 6, 7, 8]

范例2:

>> a = [1, 2, 3, 4]
>> a.extend([5, 6])
>> print(a)
>> a = [1, 2, 3, 4, 5, 6]

append(): It is basically used in Python to add one element.

Example 1:

>> a = [1, 2, 3, 4]
>> a.append(5)
>> print(a)
>> a = [1, 2, 3, 4, 5]

Example 2:

>> a = [1, 2, 3, 4]
>> a.append([5, 6])
>> print(a)
>> a = [1, 2, 3, 4, [5, 6]]

extend(): Where extend(), is used to merge two lists or insert multiple elements in one list.

Example 1:

>> a = [1, 2, 3, 4]
>> b = [5, 6, 7, 8]
>> a.extend(b)
>> print(a)
>> a = [1, 2, 3, 4, 5, 6, 7, 8]

Example 2:

>> a = [1, 2, 3, 4]
>> a.extend([5, 6])
>> print(a)
>> a = [1, 2, 3, 4, 5, 6]

回答 12

已经暗示但未解释的一个有趣的观点是,扩展比添加快。对于任何在内部具有append的循环,都应考虑将其替换为list.extend(processed_elements)。

请记住,添加新元素可能会导致整个列表重新定位到内存中的更好位置。如果由于一次添加1个元素而多次执行此操作,则总体性能会受到影响。在这种意义上,list.extend类似于“” .join(stringlist)。

An interesting point that has been hinted, but not explained, is that extend is faster than append. For any loop that has append inside should be considered to be replaced by list.extend(processed_elements).

Bear in mind that apprending new elements might result in the realloaction of the whole list to a better location in memory. If this is done several times because we are appending 1 element at a time, overall performance suffers. In this sense, list.extend is analogous to “”.join(stringlist).


回答 13

Append一次添加全部数据。整个数据将被添加到新创建的索引中。另一方面,extend顾名思义,扩展了当前数组。

例如

list1 = [123, 456, 678]
list2 = [111, 222]

随着append我们得到:

result = [123, 456, 678, [111, 222]]

extend我们得到:

result = [123, 456, 678, 111, 222]

Append adds the entire data at once. The whole data will be added to the newly created index. On the other hand, extend, as it name suggests, extends the current array.

For example

list1 = [123, 456, 678]
list2 = [111, 222]

With append we get:

result = [123, 456, 678, [111, 222]]

While on extend we get:

result = [123, 456, 678, 111, 222]

回答 14

一本英语词典定义的话append,并extend为:

append:在书面文档的末尾添加(某些内容)。
扩大:扩大。放大或扩大


有了这些知识,现在让我们了解

1)之间的区别appendextend

append

  • 所有Python对象原样追加到列表的末尾(即,作为列表中的最后一个元素)。
  • 结果列表可以嵌套,并包含异构元素(即列表,字符串,元组,字典,集合等)。

extend

  • 接受任何iterable作为其参数,并使列表更大
  • 结果列表始终是一维列表(即无嵌套),由于apply的结果,列表中可能包含异类元素(例如,字符,整数,浮点数)list(iterable)

2)之间的相似性appendextend

  • 两者都只是一个论点。
  • 两者都就地修改列表。
  • 结果,两个都返回None

lis = [1, 2, 3]

# 'extend' is equivalent to this
lis = lis + list(iterable)

# 'append' simply appends its argument as the last element to the list
# as long as the argument is a valid Python object
list.append(object)

An English dictionary defines the words append and extend as:

append: add (something) to the end of a written document.
extend: make larger. Enlarge or expand


With that knowledge, now let’s understand

1) The difference between append and extend

append:

  • Appends any Python object as-is to the end of the list (i.e. as a the last element in the list).
  • The resulting list may be nested and contain heterogeneous elements (i.e. list, string, tuple, dictionary, set, etc.)

extend:

  • Accepts any iterable as its argument and makes the list larger.
  • The resulting list is always one-dimensional list (i.e. no nesting) and it may contain heterogeneous elements in it (e.g. characters, integers, float) as a result of applying list(iterable).

2) Similarity between append and extend

  • Both take exactly one argument.
  • Both modify the list in-place.
  • As a result, both returns None.

Example

lis = [1, 2, 3]

# 'extend' is equivalent to this
lis = lis + list(iterable)

# 'append' simply appends its argument as the last element to the list
# as long as the argument is a valid Python object
list.append(object)

回答 15

我希望我可以对这个问题做出有益的补充。例如Info,如果您的列表存储了一个特定类型的对象,则这种情况extend不适用于该方法:在for循环中,Info每次生成一个对象并extend用于将其存储到列表中时,它将失败。异常如下所示:

TypeError:“ Info”对象不可迭代

但是,如果使用该append方法,则结果可以。因为每次使用该extend方法时,它将始终将其视为列表或任何其他集合类型,因此需要对其进行迭代,并将其放置在上一个列表之后。显然,不能迭代特定的对象。

I hope I can make a useful supplement to this question. If your list stores a specific type object, for example Info, here is a situation that extend method is not suitable: In a for loop and and generating an Info object every time and using extend to store it into your list, it will fail. The exception is like below:

TypeError: ‘Info’ object is not iterable

But if you use the append method, the result is OK. Because every time using the extend method, it will always treat it as a list or any other collection type, iterate it, and place it after the previous list. A specific object can not be iterated, obviously.


回答 16

直观区分它们

l1 = ['a', 'b', 'c']
l2 = ['d', 'e', 'f']
l1.append(l2)
l1
['a', 'b', 'c', ['d', 'e', 'f']]

就像l1在她体内复制一个身体(嵌套)一样。

# Reset l1 = ['a', 'b', 'c']
l1.extend(l2)
l1
['a', 'b', 'c', 'd', 'e', 'f']

就像两个分开的人结婚并组建了一个家庭。

此外,我还列出了所有列表方法的详尽清单供您参考。

list_methods = {'Add': {'extend', 'append', 'insert'},
                'Remove': {'pop', 'remove', 'clear'}
                'Sort': {'reverse', 'sort'},
                'Search': {'count', 'index'},
                'Copy': {'copy'},
                }

To distinguish them intuitively

l1 = ['a', 'b', 'c']
l2 = ['d', 'e', 'f']
l1.append(l2)
l1
['a', 'b', 'c', ['d', 'e', 'f']]

It’s like l1 reproduce a body inside her body(nested).

# Reset l1 = ['a', 'b', 'c']
l1.extend(l2)
l1
['a', 'b', 'c', 'd', 'e', 'f']

It’s like that two separated individuals get married and construct an united family.

Besides I make an exhaustive cheatsheet of all list’s methods for your reference.

list_methods = {'Add': {'extend', 'append', 'insert'},
                'Remove': {'pop', 'remove', 'clear'}
                'Sort': {'reverse', 'sort'},
                'Search': {'count', 'index'},
                'Copy': {'copy'},
                }

回答 17

extend(L)通过在给定列表中追加所有项目来扩展列表L

>>> a
[1, 2, 3]
a.extend([4])  #is eqivalent of a[len(a):] = [4]
>>> a
[1, 2, 3, 4]
a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> a[len(a):] = [4]
>>> a
[1, 2, 3, 4]

extend(L) extends the list by appending all the items in the given list L.

>>> a
[1, 2, 3]
a.extend([4])  #is eqivalent of a[len(a):] = [4]
>>> a
[1, 2, 3, 4]
a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> a[len(a):] = [4]
>>> a
[1, 2, 3, 4]

回答 18

append列表仅将一项 “扩展”(就地),即传递的单个对象(作为参数)。

extend“扩展”的名单(到位)尽可能多的项目对象传递(作为参数)包含的内容。

这可能会使str对象有些混乱。

  1. 如果您将字符串作为参数传递: append将在末尾添加单个字符串项,但 extend将添加与该字符串的长度一样多的“单个”“ str”项。
  2. 如果您将字符串列表作为参数传递:: append仍将在末尾添加单个“列表”项, extend并将添加与所传递列表的长度一样多的“列表”项。
def append_o(a_list, element):
    a_list.append(element)
    print('append:', end = ' ')
    for item in a_list:
        print(item, end = ',')
    print()

def extend_o(a_list, element):
    a_list.extend(element)
    print('extend:', end = ' ')
    for item in a_list:
        print(item, end = ',')
    print()
append_o(['ab'],'cd')

extend_o(['ab'],'cd')
append_o(['ab'],['cd', 'ef'])
extend_o(['ab'],['cd', 'ef'])
append_o(['ab'],['cd'])
extend_o(['ab'],['cd'])

生成:

append: ab,cd,
extend: ab,c,d,
append: ab,['cd', 'ef'],
extend: ab,cd,ef,
append: ab,['cd'],
extend: ab,cd,

append “extends” the list (in place) by only one item, the single object passed (as argument).

extend “extends” the list (in place) by as many items as the object passed (as argument) contains.

This may be slightly confusing for str objects.

  1. If you pass a string as argument: append will add a single string item at the end but extend will add as many “single” ‘str’ items as the length of that string.
  2. If you pass a list of strings as argument: append will still add a single ‘list’ item at the end and extend will add as many ‘list’ items as the length of the passed list.
def append_o(a_list, element):
    a_list.append(element)
    print('append:', end = ' ')
    for item in a_list:
        print(item, end = ',')
    print()

def extend_o(a_list, element):
    a_list.extend(element)
    print('extend:', end = ' ')
    for item in a_list:
        print(item, end = ',')
    print()
append_o(['ab'],'cd')

extend_o(['ab'],'cd')
append_o(['ab'],['cd', 'ef'])
extend_o(['ab'],['cd', 'ef'])
append_o(['ab'],['cd'])
extend_o(['ab'],['cd'])

produces:

append: ab,cd,
extend: ab,c,d,
append: ab,['cd', 'ef'],
extend: ab,cd,ef,
append: ab,['cd'],
extend: ab,cd,

回答 19

追加和扩展是python中的可扩展性机制之一。

追加:将元素添加到列表的末尾。

my_list = [1,2,3,4]

要向列表中添加新元素,我们可以通过以下方式使用append方法。

my_list.append(5)

将要添加新元素的默认位置始终位于(length + 1)位置。

插入:使用插入方法来克服附加的限制。使用insert,我们可以显式定义要在其中插入新元素的确切位置。

insert(index,object)的方法描述符。它有两个参数,第一个是我们要插入元素的索引,第二个是元素本身。

Example: my_list = [1,2,3,4]
my_list[4, 'a']
my_list
[1,2,3,4,'a']

扩展:当我们要将两个或多个列表合并为一个列表时,这非常有用。如果不扩展,如果我们要连接两个列表,则生成的对象将包含一个列表列表。

a = [1,2]
b = [3]
a.append(b)
print (a)
[1,2,[3]]

如果尝试访问位置2的元素,则会得到一个列表([3]),而不是元素。要加入两个列表,我们必须使用append。

a = [1,2]
b = [3]
a.extend(b)
print (a)
[1,2,3]

加入多个列表

a = [1]
b = [2]
c = [3]
a.extend(b+c)
print (a)
[1,2,3]

Append and extend are one of the extensibility mechanisms in python.

Append: Adds an element to the end of the list.

my_list = [1,2,3,4]

To add a new element to the list, we can use append method in the following way.

my_list.append(5)

The default location that the new element will be added is always in the (length+1) position.

Insert: The insert method was used to overcome the limitations of append. With insert, we can explicitly define the exact position we want our new element to be inserted at.

Method descriptor of insert(index, object). It takes two arguments, first being the index we want to insert our element and second the element itself.

Example: my_list = [1,2,3,4]
my_list[4, 'a']
my_list
[1,2,3,4,'a']

Extend: This is very useful when we want to join two or more lists into a single list. Without extend, if we want to join two lists, the resulting object will contain a list of lists.

a = [1,2]
b = [3]
a.append(b)
print (a)
[1,2,[3]]

If we try to access the element at pos 2, we get a list ([3]), instead of the element. To join two lists, we’ll have to use append.

a = [1,2]
b = [3]
a.extend(b)
print (a)
[1,2,3]

To join multiple lists

a = [1]
b = [2]
c = [3]
a.extend(b+c)
print (a)
[1,2,3]

在函数中使用全局变量

问题:在函数中使用全局变量

如何在函数中创建或使用全局变量?

如果在一个函数中创建全局变量,如何在另一个函数中使用该全局变量?我是否需要将全局变量存储在需要对其进行访问的函数的局部变量中?

How can I create or use a global variable in a function?

If I create a global variable in one function, how can I use that global variable in another function? Do I need to store the global variable in a local variable of the function which needs its access?


回答 0

您可以在其他函数中使用全局变量,方法是像global在分配给它的每个函数中一样声明它:

globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print(globvar)     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1

我想这是因为全局变量是如此危险,Python希望通过显式要求使用global关键字来确保您真正知道这就是要使用的内容。

如果要在模块之间共享全局变量,请参见其他答案。

You can use a global variable in other functions by declaring it as global in each function that assigns to it:

globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print(globvar)     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1

I imagine the reason for it is that, since global variables are so dangerous, Python wants to make sure that you really know that’s what you’re playing with by explicitly requiring the global keyword.

See other answers if you want to share a global variable across modules.


回答 1

如果我正确地理解了您的情况,那么您所看到的是Python处理本地(函数)和全局(模块)命名空间的结果。

假设您有一个像这样的模块:

# sample.py
myGlobal = 5

def func1():
    myGlobal = 42

def func2():
    print myGlobal

func1()
func2()

您可能希望它显示为42,但是它显示为5。如前所述,如果在中添加’ global‘声明func1()func2()则将输出42。

def func1():
    global myGlobal
    myGlobal = 42

这里发生的事情是,Python假定在函数内的任何位置分配给的任何名称都是该函数的本地名称,除非另有明确说明。如果仅从名称读取,并且该名称在本地不存在,它将尝试在任何包含的作用域(例如,模块的全局作用域)中查找该名称。

myGlobal因此,当您为name分配42时,Python将创建一个局部变量,该局部变量遮盖同名的全局变量。该局部变量超出范围并在返回时被垃圾回收func1();同时,func2()除了(未修改的)全局名称外,再也看不到其他任何内容。请注意,此命名空间决定是在编译时发生的,而不是在运行时发生的-如果在分配它之前先读取myGlobalinside 的值func1(),则会得到一个UnboundLocalError,因为Python已经确定它必须是局部变量,但它尚未具有任何关联的价值。但是通过使用’ global‘语句,您告诉Python应该在其他地方查找该名称,而不是在本地分配它。

(我相信这种行为主要是通过优化本地命名空间而引起的-如果没有这种行为,Python的VM每次在函数内部分配新名称时都需要至少执行三个名称查找(以确保名称没有t已存在于模块/内置级别),这会大大减慢非常常见的操作的速度。)

If I’m understanding your situation correctly, what you’re seeing is the result of how Python handles local (function) and global (module) namespaces.

Say you’ve got a module like this:

# sample.py
myGlobal = 5

def func1():
    myGlobal = 42

def func2():
    print myGlobal

func1()
func2()

You might expecting this to print 42, but instead it prints 5. As has already been mentioned, if you add a ‘global‘ declaration to func1(), then func2() will print 42.

def func1():
    global myGlobal
    myGlobal = 42

What’s going on here is that Python assumes that any name that is assigned to, anywhere within a function, is local to that function unless explicitly told otherwise. If it is only reading from a name, and the name doesn’t exist locally, it will try to look up the name in any containing scopes (e.g. the module’s global scope).

When you assign 42 to the name myGlobal, therefore, Python creates a local variable that shadows the global variable of the same name. That local goes out of scope and is garbage-collected when func1() returns; meanwhile, func2() can never see anything other than the (unmodified) global name. Note that this namespace decision happens at compile time, not at runtime — if you were to read the value of myGlobal inside func1() before you assign to it, you’d get an UnboundLocalError, because Python has already decided that it must be a local variable but it has not had any value associated with it yet. But by using the ‘global‘ statement, you tell Python that it should look elsewhere for the name instead of assigning to it locally.

(I believe that this behavior originated largely through an optimization of local namespaces — without this behavior, Python’s VM would need to perform at least three name lookups each time a new name is assigned to inside a function (to ensure that the name didn’t already exist at module/builtin level), which would significantly slow down a very common operation.)


回答 2

您可能想探索命名空间的概念。在Python中,该模块全局数据的自然存放位置:

每个模块都有自己的专用符号表,模块中定义的所有功能都将其用作全局符号表。因此,模块的作者可以在模块中使用全局变量,而不必担心与用户的全局变量的意外冲突。另一方面,如果您知道自己在做什么,则可以使用与引用其功能相同的符号来触摸模块的全局变量modname.itemname

此处描述了模块中全局变量的特定用法- 如何在模块之间共享全局变量?,为了完整起见,这里共享内容:

在单个程序内的模块之间共享信息的规范方法是创建一个特殊的配置模块(通常称为configcfg)。只需将配置模块导入应用程序的所有模块中即可;然后该模块就可以作为全局名称使用。因为每个模块只有一个实例,所以对模块对象所做的任何更改都会在所有地方反映出来。例如:

文件: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

You may want to explore the notion of namespaces. In Python, the module is the natural place for global data:

Each module has its own private symbol table, which is used as the global symbol table by all functions defined in the module. Thus, the author of a module can use global variables in the module without worrying about accidental clashes with a user’s global variables. On the other hand, if you know what you are doing you can touch a module’s global variables with the same notation used to refer to its functions, modname.itemname.

A specific use of global-in-a-module is described here – How do I share global variables across modules?, and for completeness the contents are shared here:

The canonical way to share information across modules within a single program is to create a special configuration module (often called config or cfg). Just import the configuration module in all modules of your application; the module then becomes available as a global name. Because there is only one instance of each module, any changes made to the module object get reflected everywhere. For example:

File: config.py

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

File: mod.py

import config
config.x = 1

File: main.py

import config
import mod
print config.x

回答 3

Python使用一种简单的启发式方法来确定应从本地和全局加载变量的范围。如果变量名称出现在分配的左侧,但未声明为全局变量,则假定它是局部变量。如果它没有出现在作业的左侧,则假定它是全局的。

>>> import dis
>>> def foo():
...     global bar
...     baz = 5
...     print bar
...     print baz
...     print quux
... 
>>> dis.disassemble(foo.func_code)
  3           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (baz)

  4           6 LOAD_GLOBAL              0 (bar)
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  5          11 LOAD_FAST                0 (baz)
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       

  6          16 LOAD_GLOBAL              1 (quux)
             19 PRINT_ITEM          
             20 PRINT_NEWLINE       
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        
>>> 

查看baz如何出现在赋值的左侧foo(),它是唯一的LOAD_FAST变量。

Python uses a simple heuristic to decide which scope it should load a variable from, between local and global. If a variable name appears on the left hand side of an assignment, but is not declared global, it is assumed to be local. If it does not appear on the left hand side of an assignment, it is assumed to be global.

>>> import dis
>>> def foo():
...     global bar
...     baz = 5
...     print bar
...     print baz
...     print quux
... 
>>> dis.disassemble(foo.func_code)
  3           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (baz)

  4           6 LOAD_GLOBAL              0 (bar)
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  5          11 LOAD_FAST                0 (baz)
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       

  6          16 LOAD_GLOBAL              1 (quux)
             19 PRINT_ITEM          
             20 PRINT_NEWLINE       
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        
>>> 

See how baz, which appears on the left side of an assignment in foo(), is the only LOAD_FAST variable.


回答 4

如果要在函数中引用全局变量,则可以使用global关键字声明哪些变量是全局变量。您不必在所有情况下都使用它(因为这里的人不正确地声称)-如果在本地作用域或定义此函数的函数的作用域中找不到表达式中引用的名称,则在全局范围内查找该名称变量。

但是,如果分配给未在函数中声明为全局变量的新变量,则该变量将隐式声明为局部变量,并且可能使任何现有的具有相同名称的全局变量都模糊不清。

同样,全局变量是有用的,这与某些OOP狂热者相反,特别是对于较小的脚本(OOP过于杀伤)更是如此。

If you want to refer to a global variable in a function, you can use the global keyword to declare which variables are global. You don’t have to use it in all cases (as someone here incorrectly claims) – if the name referenced in an expression cannot be found in local scope or scopes in the functions in which this function is defined, it is looked up among global variables.

However, if you assign to a new variable not declared as global in the function, it is implicitly declared as local, and it can overshadow any existing global variable with the same name.

Also, global variables are useful, contrary to some OOP zealots who claim otherwise – especially for smaller scripts, where OOP is overkill.


回答 5

如果在一个函数中创建全局变量,如何在另一个函数中使用该变量?

我们可以使用以下功能创建一个全局变量:

def create_global_variable():
    global global_variable # must declare it to be a global first
    # modifications are thus reflected on the module's global scope
    global_variable = 'Foo' 

编写函数实际上不会运行其代码。所以我们调用create_global_variable函数:

>>> create_global_variable()

使用全局变量而不进行修改

只要不希望更改它所指向的对象,就可以使用它:

例如,

def use_global_variable():
    return global_variable + '!!!'

现在我们可以使用全局变量:

>>> use_global_variable()
'Foo!!!'

从函数内部修改全局变量

要将全局变量指向另一个对象,需要再次使用global关键字:

def change_global_variable():
    global global_variable
    global_variable = 'Bar'

请注意,编写此函数后,实际上对其进行更改的代码仍未运行:

>>> use_global_variable()
'Foo!!!'

因此,在调用函数之后:

>>> change_global_variable()

我们可以看到全局变量已更改。global_variable现在该名称指向'Bar'

>>> use_global_variable()
'Bar!!!'

请注意,Python中的“全局”不是真正的全局-只是模块级别的全局。因此,它仅适用于在全局模块中编写的函数。函数会记住编写它们的模块,因此当将它们导出到其他模块时,它们仍会在创建它们的模块中查找全局变量。

具有相同名称的局部变量

如果创建具有相同名称的局部变量,它将覆盖全局变量:

def use_local_with_same_name_as_global():
    # bad name for a local variable, though.
    global_variable = 'Baz' 
    return global_variable + '!!!'

>>> use_local_with_same_name_as_global()
'Baz!!!'

但是使用名称错误的局部变量不会更改全局变量:

>>> use_global_variable()
'Bar!!!'

请注意,除非您确切知道自己在做什么并且有充分的理由这样做,否则应避免使用与全局变量同名的局部变量。我还没有遇到这样的原因。

我们在课堂上得到相同的行为

后面有评论问:

如果我想在一个类内的函数内创建一个全局变量,并想在另一个类内的另一个函数内使用该变量,该怎么办?

在这里,我演示了我们在方法中的行为与常规函数中的行为相同:

class Foo:
    def foo(self):
        global global_variable
        global_variable = 'Foo'

class Bar:
    def bar(self):
        return global_variable + '!!!'

Foo().foo()

现在:

>>> Bar().bar()
'Foo!!!'

但是我建议不要使用全局变量,而应使用类属性,以避免使模块命名空间混乱。还要注意,我们self在这里不使用参数-这些可以是类方法(如果从常规cls参数中更改class属性,则很方便)或静态方法(no selfcls)。

If I create a global variable in one function, how can I use that variable in another function?

We can create a global with the following function:

def create_global_variable():
    global global_variable # must declare it to be a global first
    # modifications are thus reflected on the module's global scope
    global_variable = 'Foo' 

Writing a function does not actually run its code. So we call the create_global_variable function:

>>> create_global_variable()

Using globals without modification

You can just use it, so long as you don’t expect to change which object it points to:

For example,

def use_global_variable():
    return global_variable + '!!!'

and now we can use the global variable:

>>> use_global_variable()
'Foo!!!'

Modification of the global variable from inside a function

To point the global variable at a different object, you are required to use the global keyword again:

def change_global_variable():
    global global_variable
    global_variable = 'Bar'

Note that after writing this function, the code actually changing it has still not run:

>>> use_global_variable()
'Foo!!!'

So after calling the function:

>>> change_global_variable()

we can see that the global variable has been changed. The global_variable name now points to 'Bar':

>>> use_global_variable()
'Bar!!!'

Note that “global” in Python is not truly global – it’s only global to the module level. So it is only available to functions written in the modules in which it is global. Functions remember the module in which they are written, so when they are exported into other modules, they still look in the module in which they were created to find global variables.

Local variables with the same name

If you create a local variable with the same name, it will overshadow a global variable:

def use_local_with_same_name_as_global():
    # bad name for a local variable, though.
    global_variable = 'Baz' 
    return global_variable + '!!!'

>>> use_local_with_same_name_as_global()
'Baz!!!'

But using that misnamed local variable does not change the global variable:

>>> use_global_variable()
'Bar!!!'

Note that you should avoid using the local variables with the same names as globals unless you know precisely what you are doing and have a very good reason to do so. I have not yet encountered such a reason.

We get the same behavior in classes

A follow on comment asks:

what to do if I want to create a global variable inside a function inside a class and want to use that variable inside another function inside another class?

Here I demonstrate we get the same behavior in methods as we do in regular functions:

class Foo:
    def foo(self):
        global global_variable
        global_variable = 'Foo'

class Bar:
    def bar(self):
        return global_variable + '!!!'

Foo().foo()

And now:

>>> Bar().bar()
'Foo!!!'

But I would suggest instead of using global variables you use class attributes, to avoid cluttering the module namespace. Also note we don’t use self arguments here – these could be class methods (handy if mutating the class attribute from the usual cls argument) or static methods (no self or cls).


回答 6

除了已经存在的答案之外,还要使其更加混乱:

在Python中,仅在函数内部引用的变量是 隐式全局的。如果在函数体内的任何位置为变量分配了新值,则假定该变量为local。如果在函数内部为变量分配了新值,则该变量是隐式局部变量,您需要将其显式声明为“ global”。

尽管起初有些令人惊讶,但片刻的考虑可以解释这一点。一方面,要求全局分配变量可防止意外副作用。另一方面,如果所有全局引用都需要全局,那么您将一直使用全局。您必须将对内置函数或导入模块的组件的每个引用声明为全局引用。这种混乱将破坏全球宣言对确定副作用的有用性。

资料来源:Python中局部和全局变量的规则是什么?

In addition to already existing answers and to make this more confusing:

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’.

Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.

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


回答 7

使用并行执行,如果您不了解发生了什么,全局变量可能会导致意外结果。这是在多处理中使用全局变量的示例。我们可以清楚地看到,每个进程都使用其自己的变量副本:

import multiprocessing
import os
import random
import sys
import time

def worker(new_value):
    old_value = get_value()
    set_value(random.randint(1, 99))
    print('pid=[{pid}] '
          'old_value=[{old_value:2}] '
          'new_value=[{new_value:2}] '
          'get_value=[{get_value:2}]'.format(
          pid=str(os.getpid()),
          old_value=old_value,
          new_value=new_value,
          get_value=get_value()))

def get_value():
    global global_variable
    return global_variable

def set_value(new_value):
    global global_variable
    global_variable = new_value

global_variable = -1

print('before set_value(), get_value() = [%s]' % get_value())
set_value(new_value=-2)
print('after  set_value(), get_value() = [%s]' % get_value())

processPool = multiprocessing.Pool(processes=5)
processPool.map(func=worker, iterable=range(15))

输出:

before set_value(), get_value() = [-1]
after  set_value(), get_value() = [-2]
pid=[53970] old_value=[-2] new_value=[ 0] get_value=[23]
pid=[53971] old_value=[-2] new_value=[ 1] get_value=[42]
pid=[53970] old_value=[23] new_value=[ 4] get_value=[50]
pid=[53970] old_value=[50] new_value=[ 6] get_value=[14]
pid=[53971] old_value=[42] new_value=[ 5] get_value=[31]
pid=[53972] old_value=[-2] new_value=[ 2] get_value=[44]
pid=[53973] old_value=[-2] new_value=[ 3] get_value=[94]
pid=[53970] old_value=[14] new_value=[ 7] get_value=[21]
pid=[53971] old_value=[31] new_value=[ 8] get_value=[34]
pid=[53972] old_value=[44] new_value=[ 9] get_value=[59]
pid=[53973] old_value=[94] new_value=[10] get_value=[87]
pid=[53970] old_value=[21] new_value=[11] get_value=[21]
pid=[53971] old_value=[34] new_value=[12] get_value=[82]
pid=[53972] old_value=[59] new_value=[13] get_value=[ 4]
pid=[53973] old_value=[87] new_value=[14] get_value=[70]

With parallel execution, global variables can cause unexpected results if you don’t understand what is happening. Here is an example of using a global variable within multiprocessing. We can clearly see that each process works with its own copy of the variable:

import multiprocessing
import os
import random
import sys
import time

def worker(new_value):
    old_value = get_value()
    set_value(random.randint(1, 99))
    print('pid=[{pid}] '
          'old_value=[{old_value:2}] '
          'new_value=[{new_value:2}] '
          'get_value=[{get_value:2}]'.format(
          pid=str(os.getpid()),
          old_value=old_value,
          new_value=new_value,
          get_value=get_value()))

def get_value():
    global global_variable
    return global_variable

def set_value(new_value):
    global global_variable
    global_variable = new_value

global_variable = -1

print('before set_value(), get_value() = [%s]' % get_value())
set_value(new_value=-2)
print('after  set_value(), get_value() = [%s]' % get_value())

processPool = multiprocessing.Pool(processes=5)
processPool.map(func=worker, iterable=range(15))

Output:

before set_value(), get_value() = [-1]
after  set_value(), get_value() = [-2]
pid=[53970] old_value=[-2] new_value=[ 0] get_value=[23]
pid=[53971] old_value=[-2] new_value=[ 1] get_value=[42]
pid=[53970] old_value=[23] new_value=[ 4] get_value=[50]
pid=[53970] old_value=[50] new_value=[ 6] get_value=[14]
pid=[53971] old_value=[42] new_value=[ 5] get_value=[31]
pid=[53972] old_value=[-2] new_value=[ 2] get_value=[44]
pid=[53973] old_value=[-2] new_value=[ 3] get_value=[94]
pid=[53970] old_value=[14] new_value=[ 7] get_value=[21]
pid=[53971] old_value=[31] new_value=[ 8] get_value=[34]
pid=[53972] old_value=[44] new_value=[ 9] get_value=[59]
pid=[53973] old_value=[94] new_value=[10] get_value=[87]
pid=[53970] old_value=[21] new_value=[11] get_value=[21]
pid=[53971] old_value=[34] new_value=[12] get_value=[82]
pid=[53972] old_value=[59] new_value=[13] get_value=[ 4]
pid=[53973] old_value=[87] new_value=[14] get_value=[70]

回答 8

您所说的是使用这样的方法:

globvar = 5

def f():
    var = globvar
    print(var)

f()  # Prints 5

但是更好的方法是像这样使用全局变量:

globavar = 5
def f():
    global globvar
    print(globvar)
f()   #prints 5

两者给出相同的输出。

What you are saying is to use the method like this:

globvar = 5

def f():
    var = globvar
    print(var)

f()  # Prints 5

But the better way is to use the global variable like this:

globavar = 5
def f():
    global globvar
    print(globvar)
f()   #prints 5

Both give the same output.


回答 9

事实证明,答案总是很简单。

这是一个小示例模块,具有在main定义中显示它的简单方法:

def five(enterAnumber,sumation):
    global helper
    helper  = enterAnumber + sumation

def isTheNumber():
    return helper

这是如何在main定义中显示它:

import TestPy

def main():
    atest  = TestPy
    atest.five(5,8)
    print(atest.isTheNumber())

if __name__ == '__main__':
    main()

这个简单的代码就是这样,它将执行。希望对您有所帮助。

As it turns out the answer is always simple.

Here is a small sample module with a simple way to show it in a main definition:

def five(enterAnumber,sumation):
    global helper
    helper  = enterAnumber + sumation

def isTheNumber():
    return helper

Here is how to show it in a main definition:

import TestPy

def main():
    atest  = TestPy
    atest.five(5,8)
    print(atest.isTheNumber())

if __name__ == '__main__':
    main()

This simple code works just like that, and it will execute. I hope it helps.


回答 10

您需要在要使用的每个函数中引用全局变量。

如下:

var = "test"

def printGlobalText():
    global var #wWe are telling to explicitly use the global version
    var = "global from printGlobalText fun."
    print "var from printGlobalText: " + var

def printLocalText():
    #We are NOT telling to explicitly use the global version, so we are creating a local variable
    var = "local version from printLocalText fun"
    print "var from printLocalText: " + var

printGlobalText()
printLocalText()
"""
Output Result:
var from printGlobalText: global from printGlobalText fun.
var from printLocalText: local version from printLocalText
[Finished in 0.1s]
"""

You need to reference the global variable in every function you want to use.

As follows:

var = "test"

def printGlobalText():
    global var #wWe are telling to explicitly use the global version
    var = "global from printGlobalText fun."
    print "var from printGlobalText: " + var

def printLocalText():
    #We are NOT telling to explicitly use the global version, so we are creating a local variable
    var = "local version from printLocalText fun"
    print "var from printLocalText: " + var

printGlobalText()
printLocalText()
"""
Output Result:
var from printGlobalText: global from printGlobalText fun.
var from printLocalText: local version from printLocalText
[Finished in 0.1s]
"""

回答 11

您实际上并没有将全局变量存储在本地变量中,而只是创建了对原始全局引用所引用的同一对象的本地引用。请记住,Python中的几乎所有内容都是一个引用对象的名称,在常规操作中不会复制任何内容。

如果不必显式指定标识符何时引用预定义的全局变量,则可能必须显式指定何时标识符是新的局部变量(例如,使用类似“ var”命令的东西)在JavaScript中看到)。由于局部变量在任何严重且不平凡的系统中都比全局变量更普遍,因此在大多数情况下,Python的系统更为有意义。

可能有一种尝试进行猜测的语言,如果存在则使用全局变量,如果不存在则创建本地变量。但是,这很容易出错。例如,导入另一个模块可能会无意中通过该名称引入全局变量,从而改变程序的行为。

You’re not actually storing the global in a local variable, just creating a local reference to the same object that your original global reference refers to. Remember that pretty much everything in Python is a name referring to an object, and nothing gets copied in usual operation.

If you didn’t have to explicitly specify when an identifier was to refer to a predefined global, then you’d presumably have to explicitly specify when an identifier is a new local variable instead (for example, with something like the ‘var’ command seen in JavaScript). Since local variables are more common than global variables in any serious and non-trivial system, Python’s system makes more sense in most cases.

You could have a language which attempted to guess, using a global variable if it existed or creating a local variable if it didn’t. However, that would be very error-prone. For example, importing another module could inadvertently introduce a global variable by that name, changing the behaviour of your program.


回答 12

尝试这个:

def x1():
    global x
    x = 6

def x2():
    global x
    x = x+1
    print x

x = 5
x1()
x2()  # output --> 7

Try this:

def x1():
    global x
    x = 6

def x2():
    global x
    x = x+1
    print x

x = 5
x1()
x2()  # output --> 7

回答 13

如果您有一个具有相同名称的局部变量,则可能要使用globals()函数

globals()['your_global_var'] = 42

In case you have a local variable with the same name, you might want to use the globals() function.

globals()['your_global_var'] = 42

回答 14

接下来,作为附加,使用文件包含所有在本地声明的所有全局变量,然后import as

文件initval.py

Stocksin = 300
Prices = []

文件getstocks.py

import initval as iv

def getmystocks(): 
    iv.Stocksin = getstockcount()


def getmycharts():
    for ic in range(iv.Stocksin):

Following on and as an add on, use a file to contain all global variables all declared locally and then import as:

File initval.py:

Stocksin = 300
Prices = []

File getstocks.py:

import initval as iv

def getmystocks(): 
    iv.Stocksin = getstockcount()


def getmycharts():
    for ic in range(iv.Stocksin):

回答 15

写入全局数组的显式元素显然不需要全局声明,尽管对其进行“批发”确实具有该要求:

import numpy as np

hostValue = 3.14159
hostArray = np.array([2., 3.])
hostMatrix = np.array([[1.0, 0.0],[ 0.0, 1.0]])

def func1():
    global hostValue    # mandatory, else local.
    hostValue = 2.0

def func2():
    global hostValue    # mandatory, else UnboundLocalError.
    hostValue += 1.0

def func3():
    global hostArray    # mandatory, else local.
    hostArray = np.array([14., 15.])

def func4():            # no need for globals
    hostArray[0] = 123.4

def func5():            # no need for globals
    hostArray[1] += 1.0

def func6():            # no need for globals
    hostMatrix[1][1] = 12.

def func7():            # no need for globals
    hostMatrix[0][0] += 0.33

func1()
print "After func1(), hostValue = ", hostValue
func2()
print "After func2(), hostValue = ", hostValue
func3()
print "After func3(), hostArray = ", hostArray
func4()
print "After func4(), hostArray = ", hostArray
func5()
print "After func5(), hostArray = ", hostArray
func6()
print "After func6(), hostMatrix = \n", hostMatrix
func7()
print "After func7(), hostMatrix = \n", hostMatrix

Writing to explicit elements of a global array does not apparently need the global declaration, though writing to it “wholesale” does have that requirement:

import numpy as np

hostValue = 3.14159
hostArray = np.array([2., 3.])
hostMatrix = np.array([[1.0, 0.0],[ 0.0, 1.0]])

def func1():
    global hostValue    # mandatory, else local.
    hostValue = 2.0

def func2():
    global hostValue    # mandatory, else UnboundLocalError.
    hostValue += 1.0

def func3():
    global hostArray    # mandatory, else local.
    hostArray = np.array([14., 15.])

def func4():            # no need for globals
    hostArray[0] = 123.4

def func5():            # no need for globals
    hostArray[1] += 1.0

def func6():            # no need for globals
    hostMatrix[1][1] = 12.

def func7():            # no need for globals
    hostMatrix[0][0] += 0.33

func1()
print "After func1(), hostValue = ", hostValue
func2()
print "After func2(), hostValue = ", hostValue
func3()
print "After func3(), hostArray = ", hostArray
func4()
print "After func4(), hostArray = ", hostArray
func5()
print "After func5(), hostArray = ", hostArray
func6()
print "After func6(), hostMatrix = \n", hostMatrix
func7()
print "After func7(), hostMatrix = \n", hostMatrix

回答 16

我添加此内容是因为我在其他任何答案中都没有看到它,这对于那些在类似问题上苦苦挣扎的人可能很有用。该globals()函数返回一个可变的全局符号字典,您可以在其中“神奇地”使数据可用于其余代码。例如:

from pickle import load
def loaditem(name):
    with open(r"C:\pickle\file\location"+"\{}.dat".format(name), "rb") as openfile:
        globals()[name] = load(openfile)
    return True

from pickle import dump
def dumpfile(name):
    with open(name+".dat", "wb") as outfile:
        dump(globals()[name], outfile)
    return True

只会让您将变量转储/加载到全局命名空间中。超级方便,没有混乱,没有大惊小怪。可以肯定,它仅适用于Python 3。

I’m adding this as I haven’t seen it in any of the other answers and it might be useful for someone struggling with something similar. The globals() function returns a mutable global symbol dictionary where you can “magically” make data available for the rest of your code. For example:

from pickle import load
def loaditem(name):
    with open(r"C:\pickle\file\location"+"\{}.dat".format(name), "rb") as openfile:
        globals()[name] = load(openfile)
    return True

and

from pickle import dump
def dumpfile(name):
    with open(name+".dat", "wb") as outfile:
        dump(globals()[name], outfile)
    return True

Will just let you dump/load variables out of and into the global namespace. Super convenient, no muss, no fuss. Pretty sure it’s Python 3 only.


回答 17

引用要在其中显示更改的类命名空间。

在此示例中,Runner正在使用文件配置中的max。我希望我的测试在跑步者使用它时更改max的值。

main / config.py

max = 15000

main / runner.py

from main import config
def check_threads():
    return max < thread_count 

测试/runner_test.py

from main import runner                # <----- 1. add file
from main.runner import check_threads
class RunnerTest(unittest):
   def test_threads(self):
       runner.max = 0                  # <----- 2. set global 
       check_threads()

Reference the class namespace where you want the change to show up.

In this example, runner is using max from the file config. I want my test to change the value of max when runner is using it.

main/config.py

max = 15000

main/runner.py

from main import config
def check_threads():
    return max < thread_count 

tests/runner_test.py

from main import runner                # <----- 1. add file
from main.runner import check_threads
class RunnerTest(unittest):
   def test_threads(self):
       runner.max = 0                  # <----- 2. set global 
       check_threads()

回答 18

全局变量很好-多重处理除外

与不同平台/环境的多处理相关的全局问题(一方面是Windows / Mac OS,另一方面是Linux)很麻烦。

我将通过一个简单的示例向您展示这个问题,该问题指出了我前一段时间遇到的问题。

如果您想了解Windows / MacO和Linux上为何有所不同的原因,那么您需要知道这是在…上启动新进程的默认机制。

  • Windows / MacO是“生成”的
  • Linux是“ fork”

它们在内存分配和初始化方面有所不同…(但在此不做介绍)。

让我们看一下问题/示例…

import multiprocessing

counter = 0

def do(task_id):
    global counter
    counter +=1
    print(f'task {task_id}: counter = {counter}')

if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=4)
    task_ids = list(range(4))
    pool.map(do, task_ids)

视窗

如果您在Windows上运行此程序(我也想在MacOS上运行),则会得到以下输出…

task 0: counter = 1
task 1: counter = 2
task 2: counter = 3
task 3: counter = 4

的Linux

如果在Linux上运行它,则会得到以下内容。

task 0: counter = 1
task 1: counter = 1
task 2: counter = 1
task 3: counter = 1

Globals are fine – Except with Multiprocessing

Globals in connection with multiprocessing on different platforms/envrionments as Windows/Mac OS on the one side and Linux on the other are troublesome.

I will show you this with a simple example pointing out a problem which I run into some time ago.

If you want to understand, why things are different on Windows/MacOs and Linux you need to know that, the default mechanism to start a new process on …

  • Windows/MacOs is ‘spawn’
  • Linux is ‘fork’

They are different in Memory allocation an initialisation … (but I don’t go into this here).

Let’s have a look at the problem/example …

import multiprocessing

counter = 0

def do(task_id):
    global counter
    counter +=1
    print(f'task {task_id}: counter = {counter}')

if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=4)
    task_ids = list(range(4))
    pool.map(do, task_ids)

Windows

If you run this on Windows (And I suppose on MacOS too), you get the following output …

task 0: counter = 1
task 1: counter = 2
task 2: counter = 3
task 3: counter = 4

Linux

If you run this on Linux, you get the following instead.

task 0: counter = 1
task 1: counter = 1
task 2: counter = 1
task 3: counter = 1

如何在Python中获取当前时间

问题:如何在Python中获取当前时间

获取当前时间的模块/方法是什么?

What is the module/method used to get the current time?


回答 0

采用:

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2009, 1, 6, 15, 8, 24, 78915)

>>> print(datetime.datetime.now())
2009-01-06 15:08:24.789150

而只是时间:

>>> datetime.datetime.now().time()
datetime.time(15, 8, 24, 78915)

>>> print(datetime.datetime.now().time())
15:08:24.789150

请参阅文档以获取更多信息。

要保存输入,可以datetimedatetime模块中导入对象:

>>> from datetime import datetime

然后datetime.从以上所有位置移除引线。

Use:

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2009, 1, 6, 15, 8, 24, 78915)

>>> print(datetime.datetime.now())
2009-01-06 15:08:24.789150

And just the time:

>>> datetime.datetime.now().time()
datetime.time(15, 8, 24, 78915)

>>> print(datetime.datetime.now().time())
15:08:24.789150

See the documentation for more information.

To save typing, you can import the datetime object from the datetime module:

>>> from datetime import datetime

Then remove the leading datetime. from all of the above.


回答 1

您可以使用time.strftime()

>>> from time import gmtime, strftime
>>> strftime("%Y-%m-%d %H:%M:%S", gmtime())
'2009-01-05 22:14:39'

You can use time.strftime():

>>> from time import gmtime, strftime
>>> strftime("%Y-%m-%d %H:%M:%S", gmtime())
'2009-01-05 22:14:39'

回答 2

from datetime import datetime
datetime.now().strftime('%Y-%m-%d %H:%M:%S')

对于此示例,输出将如下所示: '2013-09-18 11:16:32'

这是strftime指令列表。

from datetime import datetime
datetime.now().strftime('%Y-%m-%d %H:%M:%S')

For this example, the output will be like this: '2013-09-18 11:16:32'

Here is the list of strftime directives.


回答 3

Harley的答案相似,但使用该str()函数可得到快速n脏的,人类可读的格式:

>>> from datetime import datetime
>>> str(datetime.now())
'2011-05-03 17:45:35.177000'

Similar to Harley’s answer, but use the str() function for a quick-n-dirty, slightly more human readable format:

>>> from datetime import datetime
>>> str(datetime.now())
'2011-05-03 17:45:35.177000'

回答 4

如何使用Python获取当前时间?

time模块

time模块提供的功能可以告诉我们时间(自纪元以来的秒数)以及其他实用程序。

import time

Unix时代时间

这是保存数据库时应使用的时间戳格式。它是一个简单的浮点数,可以转换为整数。这对于以秒为单位的算术也很有用,因为它代表自1970年1月1日00:00:00以来的秒数,并且相对于我们接下来要看的其他时间表示,它是记忆光。

>>> time.time()
1424233311.771502

该时间戳记不占leap秒,因此不是线性的-leap秒将被忽略。因此,尽管它不等同于国际UTC标准,但它很接近,因此对于大多数记录保存情况而言,这是相当好的。

但是,这对于人工调度而言并不理想。如果您希望在某个特定时间发生将来的事件,则需要使用可以解析为datetime对象或序列化datetime对象的字符串存储该时间(稍后将对此进行描述)。

time.ctime

您还可以用操作系统首选的方式来表示当前时间(这意味着当您更改系统首选项时,它可以更改,因此请不要像其他人所期望的那样,将其作为所有系统的标准时间) 。这通常是用户友好的,但通常不会导致字符串可以按时间顺序排序:

>>> time.ctime()
'Tue Feb 17 23:21:56 2015'

您还可以通过以下方式将时间戳混合为易于阅读的形式ctime

>>> time.ctime(1424233311.771502)
'Tue Feb 17 23:21:51 2015'

这种转换也不利于保存记录(除非只能由人类解析的文本,并且随着光学字符识别和人工智能的改进,我认为这些案件的数量将会减少)。

datetime 模组

datetime模块在这里也非常有用:

>>> import datetime

datetime.datetime.now

datetime.now是一类方法,它返回当前时间。它使用time.localtime不带时区信息的(如果未提供,否则请参阅下面的了解时区)。它具有在外壳上回显的表示形式(允许您重新创建等效的对象),但是在打印(或强制转换为str)时,它具有人类可读(和几乎ISO)的格式,而词典编排相当于按时间顺序排序:

>>> datetime.datetime.now()
datetime.datetime(2015, 2, 17, 23, 43, 49, 94252)
>>> print(datetime.datetime.now())
2015-02-17 23:43:51.782461

日期时间 utcnow

通过执行以下操作,您可以获取全球标准UTC时间中的datetime对象:

>>> datetime.datetime.utcnow()
datetime.datetime(2015, 2, 18, 4, 53, 28, 394163)
>>> print(datetime.datetime.utcnow())
2015-02-18 04:53:31.783988

UTC是几乎等同于GMT时区的时间标准。(虽然GMT和UTC的夏令时不变,但他们的用户可能会在夏季切换到其他时区,例如英国夏令时。)

datetime时区感知

但是,到目前为止,我们创建的datetime对象都无法轻松转换为各种时区。我们可以使用以下pytz模块解决该问题:

>>> import pytz
>>> then = datetime.datetime.now(pytz.utc)
>>> then
datetime.datetime(2015, 2, 18, 4, 55, 58, 753949, tzinfo=<UTC>)

等效地,在Python 3中,我们有一个timezone带有utc timezone实例的类,它也使对象知道时区(但在没有方便的pytz模块的情况下,转换成另一个时区留给读者练习):

>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2015, 2, 18, 22, 31, 56, 564191, tzinfo=datetime.timezone.utc)

而且我们看到我们可以轻松地从原始utc对象转换为时区。

>>> print(then)
2015-02-18 04:55:58.753949+00:00
>>> print(then.astimezone(pytz.timezone('US/Eastern')))
2015-02-17 23:55:58.753949-05:00

您还可以使用pytztimezone localize方法或通过替换tzinfo属性(使用replace,这是盲目的完成)来使朴素的datetime对象知道的,但是这些方法比最佳实践更多的是不得已而为之:

>>> pytz.utc.localize(datetime.datetime.utcnow())
datetime.datetime(2015, 2, 18, 6, 6, 29, 32285, tzinfo=<UTC>)
>>> datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
datetime.datetime(2015, 2, 18, 6, 9, 30, 728550, tzinfo=<UTC>)

pytz模块使我们能够使datetime对象知道时区,并将时间转换为pytz模块中可用的数百个时区。

人们可以表面上连载这个对象UTC时间和存储在数据库中,但它需要远远更多的内存和比简单地存储Unix纪元的时间,这是我第一次表现出更容易出错。

其他查看时间的方式更容易出错,尤其是在处理可能来自不同时区的数据时。您希望不要将字符串或序列化日期时间对象用于哪个时区。

如果您正在使用Python为用户显示时间,则ctime可以很好地工作,而不是在表中(通常排序不佳),而是在时钟中。但是,我个人建议在Python中使用Unix时间或时区感知UTC datetime对象处理时间时。

How do I get the current time in Python?

The time module

The time module provides functions that tells us the time in “seconds since the epoch” as well as other utilities.

import time

Unix Epoch Time

This is the format you should get timestamps in for saving in databases. It is a simple floating point number that can be converted to an integer. It is also good for arithmetic in seconds, as it represents the number of seconds since Jan 1, 1970 00:00:00, and it is memory light relative to the other representations of time we’ll be looking at next:

>>> time.time()
1424233311.771502

This timestamp does not account for leap-seconds, so it’s not linear – leap seconds are ignored. So while it is not equivalent to the international UTC standard, it is close, and therefore quite good for most cases of record-keeping.

This is not ideal for human scheduling, however. If you have a future event you wish to take place at a certain point in time, you’ll want to store that time with a string that can be parsed into a datetime object or a serialized datetime object (these will be described later).

time.ctime

You can also represent the current time in the way preferred by your operating system (which means it can change when you change your system preferences, so don’t rely on this to be standard across all systems, as I’ve seen others expect). This is typically user friendly, but doesn’t typically result in strings one can sort chronologically:

>>> time.ctime()
'Tue Feb 17 23:21:56 2015'

You can hydrate timestamps into human readable form with ctime as well:

>>> time.ctime(1424233311.771502)
'Tue Feb 17 23:21:51 2015'

This conversion is also not good for record-keeping (except in text that will only be parsed by humans – and with improved Optical Character Recognition and Artificial Intelligence, I think the number of these cases will diminish).

datetime module

The datetime module is also quite useful here:

>>> import datetime

datetime.datetime.now

The datetime.now is a class method that returns the current time. It uses the time.localtime without the timezone info (if not given, otherwise see timezone aware below). It has a representation (which would allow you to recreate an equivalent object) echoed on the shell, but when printed (or coerced to a str), it is in human readable (and nearly ISO) format, and the lexicographic sort is equivalent to the chronological sort:

>>> datetime.datetime.now()
datetime.datetime(2015, 2, 17, 23, 43, 49, 94252)
>>> print(datetime.datetime.now())
2015-02-17 23:43:51.782461

datetime’s utcnow

You can get a datetime object in UTC time, a global standard, by doing this:

>>> datetime.datetime.utcnow()
datetime.datetime(2015, 2, 18, 4, 53, 28, 394163)
>>> print(datetime.datetime.utcnow())
2015-02-18 04:53:31.783988

UTC is a time standard that is nearly equivalent to the GMT timezone. (While GMT and UTC do not change for Daylight Savings Time, their users may switch to other timezones, like British Summer Time, during the Summer.)

datetime timezone aware

However, none of the datetime objects we’ve created so far can be easily converted to various timezones. We can solve that problem with the pytz module:

>>> import pytz
>>> then = datetime.datetime.now(pytz.utc)
>>> then
datetime.datetime(2015, 2, 18, 4, 55, 58, 753949, tzinfo=<UTC>)

Equivalently, in Python 3 we have the timezone class with a utc timezone instance attached, which also makes the object timezone aware (but to convert to another timezone without the handy pytz module is left as an exercise to the reader):

>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2015, 2, 18, 22, 31, 56, 564191, tzinfo=datetime.timezone.utc)

And we see we can easily convert to timezones from the original utc object.

>>> print(then)
2015-02-18 04:55:58.753949+00:00
>>> print(then.astimezone(pytz.timezone('US/Eastern')))
2015-02-17 23:55:58.753949-05:00

You can also make a naive datetime object aware with the pytz timezone localize method, or by replacing the tzinfo attribute (with replace, this is done blindly), but these are more last resorts than best practices:

>>> pytz.utc.localize(datetime.datetime.utcnow())
datetime.datetime(2015, 2, 18, 6, 6, 29, 32285, tzinfo=<UTC>)
>>> datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
datetime.datetime(2015, 2, 18, 6, 9, 30, 728550, tzinfo=<UTC>)

The pytz module allows us to make our datetime objects timezone aware and convert the times to the hundreds of timezones available in the pytz module.

One could ostensibly serialize this object for UTC time and store that in a database, but it would require far more memory and be more prone to error than simply storing the Unix Epoch time, which I demonstrated first.

The other ways of viewing times are much more error prone, especially when dealing with data that may come from different time zones. You want there to be no confusion as to which timezone a string or serialized datetime object was intended for.

If you’re displaying the time with Python for the user, ctime works nicely, not in a table (it doesn’t typically sort well), but perhaps in a clock. However, I personally recommend, when dealing with time in Python, either using Unix time, or a timezone aware UTC datetime object.


回答 5

from time import time

t = time()
  • t -浮点数,适用于时间间隔测量。

Unix和Windows平台有所不同。

Do

from time import time

t = time()
  • t – float number, good for time interval measurement.

There is some difference for Unix and Windows platforms.


回答 6

>>> from time import gmtime, strftime
>>> strftime("%a, %d %b %Y %X +0000", gmtime())
'Tue, 06 Jan 2009 04:54:56 +0000'

以指定格式输出当前GMT。还有一种localtime()方法。

页面有更多详细信息。

>>> from time import gmtime, strftime
>>> strftime("%a, %d %b %Y %X +0000", gmtime())
'Tue, 06 Jan 2009 04:54:56 +0000'

That outputs the current GMT in the specified format. There is also a localtime() method.

This page has more details.


回答 7

先前的答案都是不错的建议,但我发现它最容易使用ctime()

In [2]: from time import ctime
In [3]: ctime()
Out[3]: 'Thu Oct 31 11:40:53 2013'

这样可以很好地格式化当前本地时间的字符串表示形式。

The previous answers are all good suggestions, but I find it easiest to use ctime():

In [2]: from time import ctime
In [3]: ctime()
Out[3]: 'Thu Oct 31 11:40:53 2013'

This gives a nicely formatted string representation of the current local time.


回答 8

最快的方法是:

>>> import time
>>> time.strftime("%Y%m%d")
'20130924'

The quickest way is:

>>> import time
>>> time.strftime("%Y%m%d")
'20130924'

回答 9

如果您需要当前时间作为time对象:

>>> import datetime
>>> now = datetime.datetime.now()
>>> datetime.time(now.hour, now.minute, now.second)
datetime.time(11, 23, 44)

If you need current time as a time object:

>>> import datetime
>>> now = datetime.datetime.now()
>>> datetime.time(now.hour, now.minute, now.second)
datetime.time(11, 23, 44)

回答 10

.isoformat() 在文档中,但尚未在此处(这与@Ray Vega的答案非常相似):

>>> import datetime
>>> datetime.datetime.now().isoformat()
'2013-06-24T20:35:55.982000'

.isoformat() is in the documentation, but not yet here (this is mighty similar to @Ray Vega’s answer):

>>> import datetime
>>> datetime.datetime.now().isoformat()
'2013-06-24T20:35:55.982000'

回答 11

为什么不问美国海军的官方计时器美国海军天文台呢?

import requests
from lxml import html

page = requests.get('http://tycho.usno.navy.mil/cgi-bin/timer.pl')
tree = html.fromstring(page.content)
print(tree.xpath('//html//body//h3//pre/text()')[1])

如果您像我一样住在华盛顿特区,那么延迟可能不会太糟糕…

Why not ask the U.S. Naval Observatory, the official timekeeper of the United States Navy?

import requests
from lxml import html

page = requests.get('http://tycho.usno.navy.mil/cgi-bin/timer.pl')
tree = html.fromstring(page.content)
print(tree.xpath('//html//body//h3//pre/text()')[1])

If you live in the D.C. area (like me) the latency might not be too bad…


回答 12

使用熊猫来获取当前时间,有点过头了:

import pandas as pd
print(pd.datetime.now())
print(pd.datetime.now().date())
print(pd.datetime.now().year)
print(pd.datetime.now().month)
print(pd.datetime.now().day)
print(pd.datetime.now().hour)
print(pd.datetime.now().minute)
print(pd.datetime.now().second)
print(pd.datetime.now().microsecond)

输出:

2017-09-22 12:44:56.092642
2017-09-22
2017
9
22
12
44
56
92693

Using pandas to get the current time, kind of overkilling the problem at hand:

import pandas as pd
print(pd.datetime.now())
print(pd.datetime.now().date())
print(pd.datetime.now().year)
print(pd.datetime.now().month)
print(pd.datetime.now().day)
print(pd.datetime.now().hour)
print(pd.datetime.now().minute)
print(pd.datetime.now().second)
print(pd.datetime.now().microsecond)

Output:

2017-09-22 12:44:56.092642
2017-09-22
2017
9
22
12
44
56
92693

回答 13

这就是我最终要进行的工作:

>>>from time import strftime
>>>strftime("%m/%d/%Y %H:%M")
01/09/2015 13:11

此外,该表是选择适当的格式代码得到格式化只是你想要的方式日期(从Python的“日期时间”的文档的必要参考这里)。

strftime格式代码表

This is what I ended up going with:

>>>from time import strftime
>>>strftime("%m/%d/%Y %H:%M")
01/09/2015 13:11

Also, this table is a necessary reference for choosing the appropriate format codes to get the date formatted just the way you want it (from Python “datetime” documentation here).

strftime format code table


回答 14

如果您已经在使用numpy,则可以直接使用numpy.datetime64()函数。

import numpy as np
str(np.datetime64('now'))

仅限日期:

str(np.datetime64('today'))

或者,如果您已经在使用熊猫,则可以使用pandas.to_datetime()函数

import pandas as pd
str(pd.to_datetime('now'))

要么,

str(pd.to_datetime('today'))

if you are using numpy already then directly you can use numpy.datetime64() function.

import numpy as np
str(np.datetime64('now'))

for only date:

str(np.datetime64('today'))

or, if you are using pandas already then you can use pandas.to_datetime() function

import pandas as pd
str(pd.to_datetime('now'))

or,

str(pd.to_datetime('today'))

回答 15

您可以使用以下time模块:

import time
print time.strftime("%d/%m/%Y")

>>> 06/02/2015

资本的使用Y给出了全年,而使用则y给出了06/02/15

您还可以使用以下代码来延长时间:

time.strftime("%a, %d %b %Y %H:%M:%S")
>>> 'Fri, 06 Feb 2015 17:45:09'

You can use the time module:

import time
print time.strftime("%d/%m/%Y")

>>> 06/02/2015

The use of the capital Y gives the full year, and using y would give 06/02/15.

You could also use the following code to give a more lengthy time:

time.strftime("%a, %d %b %Y %H:%M:%S")
>>> 'Fri, 06 Feb 2015 17:45:09'

回答 16

datetime.now()返回当前时间作为朴素的datetime对象,该对象表示本地时区中的时间。该值可能不明确,例如在DST转换期间(“回退”)。为避免歧义,应使用UTC时区:

from datetime import datetime

utc_time = datetime.utcnow()
print(utc_time) # -> 2014-12-22 22:48:59.916417

或具有附加时区信息的时区感知对象(Python 3.2+):

from datetime import datetime, timezone

now = datetime.now(timezone.utc).astimezone()
print(now) # -> 2014-12-23 01:49:25.837541+03:00

datetime.now() returns the current time as a naive datetime object that represents time in the local timezone. That value may be ambiguous e.g., during DST transitions (“fall back”). To avoid ambiguity either UTC timezone should be used:

from datetime import datetime

utc_time = datetime.utcnow()
print(utc_time) # -> 2014-12-22 22:48:59.916417

Or a timezone-aware object that has the corresponding timezone info attached (Python 3.2+):

from datetime import datetime, timezone

now = datetime.now(timezone.utc).astimezone()
print(now) # -> 2014-12-23 01:49:25.837541+03:00

回答 17

import datetime
date_time = datetime.datetime.now()

date = date_time.date()  # Gives the date
time = date_time.time()  # Gives the time

print date.year, date.month, date.day
print time.hour, time.minute, time.second, time.microsecond

dir(date)或任何变量,包括包装。您可以获得与该变量关联的所有属性和方法。

import datetime
date_time = datetime.datetime.now()

date = date_time.date()  # Gives the date
time = date_time.time()  # Gives the time

print date.year, date.month, date.day
print time.hour, time.minute, time.second, time.microsecond

Do dir(date) or any variables including the package. You can get all the attributes and methods associated with the variable.


回答 18

>>> import datetime, time
>>> time = time.strftime("%H:%M:%S:%MS", time.localtime())
>>> print time
'00:21:38:20S'
>>> import datetime, time
>>> time = time.strftime("%H:%M:%S:%MS", time.localtime())
>>> print time
'00:21:38:20S'

回答 19

默认情况下,now()函数以YYYY-MM-DD HH:MM:SS:MS格式返回输出。使用以下示例脚本在Python脚本中获取当前日期和时间,并在屏幕上打印结果。创建getDateTime1.py具有以下内容的文件。

import datetime

currentDT = datetime.datetime.now()
print (str(currentDT))

输出如下所示:

2018-03-01 17:03:46.759624

By default, now() function returns output in the YYYY-MM-DD HH:MM:SS:MS format. Use the below sample script to get the current date and time in a Python script and print results on the screen. Create file getDateTime1.py with the below content.

import datetime

currentDT = datetime.datetime.now()
print (str(currentDT))

The output looks like below:

2018-03-01 17:03:46.759624

回答 20

这个问题并不需要仅仅为了它而提供一个新的答案……但是,一个闪亮的新玩具/模块就足够了。那就是Pendulum库,它似乎可以完成arrow尝试的各种工作,但没有固有的缺陷和bug困扰着arrow。

例如,原始问题的答案:

>>> import pendulum
>>> print(pendulum.now())
2018-08-14T05:29:28.315802+10:00
>>> print(pendulum.now('utc'))
2018-08-13T19:29:35.051023+00:00

有很多需要解决的标准,包括多个RFC和ISO。曾经把它们混在一起;不用担心,请看一看dir(pendulum.constants)。不过,这里还有RFC和ISO格式。

当我们说本地的时候,虽然是什么意思?好吧,我的意思是:

>>> print(pendulum.now().timezone_name)
Australia/Melbourne
>>>

大概大多数人都在别的地方。

继续下去。长话短说:Pendulum尝试在日期和时间上执行HTTP请求的操作。值得考虑,尤其是它的易用性和广泛的文档资料。

This question doesn’t need a new answer just for the sake of it … a shiny new-ish toy/module, however, is enough justification. That being the Pendulum library, which appears to do the sort of things which arrow attempted, except without the inherent flaws and bugs which beset arrow.

For instance, the answer to the original question:

>>> import pendulum
>>> print(pendulum.now())
2018-08-14T05:29:28.315802+10:00
>>> print(pendulum.now('utc'))
2018-08-13T19:29:35.051023+00:00

There’s a lot of standards which need addressing, including multiple RFCs and ISOs, to worry about. Ever get them mixed up; not to worry, take a little look into dir(pendulum.constants) There’s a bit more than RFC and ISO formats there, though.

When we say local, though what do we mean? Well I mean:

>>> print(pendulum.now().timezone_name)
Australia/Melbourne
>>>

Presumably most of the rest of you mean somewhere else.

And on it goes. Long story short: Pendulum attempts to do for date and time what requests did for HTTP. It’s worth consideration, particularly for both its ease of use and extensive documentation.


回答 21

时区的当前时间

from datetime import datetime
import pytz

tz_NY = pytz.timezone('America/New_York') 
datetime_NY = datetime.now(tz_NY)
print("NY time:", datetime_NY.strftime("%H:%M:%S"))

tz_London = pytz.timezone('Europe/London')
datetime_London = datetime.now(tz_London)
print("London time:", datetime_London.strftime("%H:%M:%S"))

tz_India = pytz.timezone('Asia/India')
datetime_India = datetime.now(tz_India)
print("India time:", datetime_India.strftime("%H:%M:%S"))

#list timezones
pytz.all_timezones

Current time of a timezone

from datetime import datetime
import pytz

tz_NY = pytz.timezone('America/New_York') 
datetime_NY = datetime.now(tz_NY)
print("NY time:", datetime_NY.strftime("%H:%M:%S"))

tz_London = pytz.timezone('Europe/London')
datetime_London = datetime.now(tz_London)
print("London time:", datetime_London.strftime("%H:%M:%S"))

tz_India = pytz.timezone('Asia/India')
datetime_India = datetime.now(tz_India)
print("India time:", datetime_India.strftime("%H:%M:%S"))

#list timezones
pytz.all_timezones

回答 22

试用http://crsmithdev.com/arrow/中的箭头模块:

import arrow
arrow.now()

或UTC版本:

arrow.utcnow()

要更改其输出,请添加.format():

arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')

对于特定时区:

arrow.now('US/Pacific')

一小时前:

arrow.utcnow().replace(hours=-1)

或者,如果您要要旨。

arrow.get('2013-05-11T21:23:58.970460+00:00').humanize()
>>> '2 years ago'

Try the arrow module from http://crsmithdev.com/arrow/:

import arrow
arrow.now()

Or the UTC version:

arrow.utcnow()

To change its output, add .format():

arrow.utcnow().format('YYYY-MM-DD HH:mm:ss ZZ')

For a specific timezone:

arrow.now('US/Pacific')

An hour ago:

arrow.utcnow().replace(hours=-1)

Or if you want the gist.

arrow.get('2013-05-11T21:23:58.970460+00:00').humanize()
>>> '2 years ago'

回答 23

我想用毫秒来获取时间。一种简单的获取方法:

import time, datetime

print(datetime.datetime.now().time())                         # 11:20:08.272239

# Or in a more complicated way
print(datetime.datetime.now().time().isoformat())             # 11:20:08.272239
print(datetime.datetime.now().time().strftime('%H:%M:%S.%f')) # 11:20:08.272239

# But do not use this:
print(time.strftime("%H:%M:%S.%f", time.localtime()), str)    # 11:20:08.%f

但是我只想毫秒,对不对?获得它们的最短方法:

import time

time.strftime("%H:%M:%S", time.localtime()) + '.%d' % (time.time() % 1 * 1000)
# 11:34:23.751

从上一个乘法中添加或删除零以调整小数点位数,或者仅:

def get_time_str(decimal_points=3):
    return time.strftime("%H:%M:%S", time.localtime()) + '.%d' % (time.time() % 1 * 10**decimal_points)

I want to get the time with milliseconds. A simple way to get them:

import time, datetime

print(datetime.datetime.now().time())                         # 11:20:08.272239

# Or in a more complicated way
print(datetime.datetime.now().time().isoformat())             # 11:20:08.272239
print(datetime.datetime.now().time().strftime('%H:%M:%S.%f')) # 11:20:08.272239

# But do not use this:
print(time.strftime("%H:%M:%S.%f", time.localtime()), str)    # 11:20:08.%f

But I want only milliseconds, right? The shortest way to get them:

import time

time.strftime("%H:%M:%S", time.localtime()) + '.%d' % (time.time() % 1 * 1000)
# 11:34:23.751

Add or remove zeroes from the last multiplication to adjust number of decimal points, or just:

def get_time_str(decimal_points=3):
    return time.strftime("%H:%M:%S", time.localtime()) + '.%d' % (time.time() % 1 * 10**decimal_points)

回答 24

您可以使用此功能获取时间(不幸的是,它没有显示AM或PM):

def gettime():
    from datetime import datetime
    return ((str(datetime.now())).split(' ')[1]).split('.')[0]

要获取以后合并的小时,分​​钟,秒和毫秒,可以使用以下功能:

小时:

def gethour():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[0]

分钟:

def getminute():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[1]

第二:

def getsecond():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[2]

毫秒:

def getmillisecond():
    from datetime import datetime
    return (str(datetime.now())).split('.')[1]

You can use this function to get the time (unfortunately it doesn’t say AM or PM):

def gettime():
    from datetime import datetime
    return ((str(datetime.now())).split(' ')[1]).split('.')[0]

To get the hours, minutes, seconds and milliseconds to merge later, you can use these functions:

Hour:

def gethour():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[0]

Minute:

def getminute():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[1]

Second:

def getsecond():
    from datetime import datetime
    return (((str(datetime.now())).split(' ')[1]).split('.')[0]).split(':')[2]

Millisecond:

def getmillisecond():
    from datetime import datetime
    return (str(datetime.now())).split('.')[1]

回答 25

如果只需要当前时间戳(以毫秒为单位)(例如,测量执行时间),则也可以使用“ timeit”模块:

import timeit
start_time = timeit.default_timer()
do_stuff_you_want_to_measure()
end_time = timeit.default_timer()
print("Elapsed time: {}".format(end_time - start_time))

If you just want the current timestamp in ms (for example, to measure execution time), you can also use the “timeit” module:

import timeit
start_time = timeit.default_timer()
do_stuff_you_want_to_measure()
end_time = timeit.default_timer()
print("Elapsed time: {}".format(end_time - start_time))

回答 26

以下是我用来获取时间而不必进行格式化的内容。有些人不喜欢split方法,但是在这里很有用:

from time import ctime
print ctime().split()[3]

它将以HH:MM:SS格式打印。

The following is what I use to get the time without having to format. Some people don’t like the split method, but it is useful here:

from time import ctime
print ctime().split()[3]

It will print in HH:MM:SS format.


回答 27

因为还没有人提及它,所以我最近遇到了这个问题……pytz时区的fromutc()方法与datetime的utcnow()结合是我发现获得有用的当前时间(和日期)的最佳方法在任何时区。

from datetime import datetime

import pytz


JST = pytz.timezone("Asia/Tokyo")


local_time = JST.fromutc(datetime.utcnow())

如果您想要的只是时间,那么您可以使用local_time.time()

Because no one has mentioned it yet, and this is something I ran into recently… a pytz timezone’s fromutc() method combined with datetime’s utcnow() is the best way I’ve found to get a useful current time (and date) in any timezone.

from datetime import datetime

import pytz


JST = pytz.timezone("Asia/Tokyo")


local_time = JST.fromutc(datetime.utcnow())

If all you want is the time, you can then get that with local_time.time().


回答 28

这个问题是针对Python的,但是由于Django是Python使用最广泛的框架之一,因此必须注意,如果您使用的是Django,则可以始终使用timezone.now()而不是datetime.datetime.now()。前者是时区“知道”的,而后者则不是。

请参阅此SO答案Django文档,以获取详细信息和背后的原理timezone.now()

from django.utils import timezone

now = timezone.now()

This question is for Python but since Django is one of the most widely used frameworks for Python, its important to note that if you are using Django you can always use timezone.now() instead of datetime.datetime.now(). The former is timezone ‘aware’ while the latter is not.

See this SO answer and the Django doc for details and rationale behind timezone.now().

from django.utils import timezone

now = timezone.now()

回答 29

您可以使用ctime()来做到这一点:

from time import time, ctime
t = time()
ctime(t)

输出:

Sat Sep 14 21:27:08 2019

这些输出是不同的,因为返回的时间戳ctime()取决于您的地理位置。

You can do so using ctime():

from time import time, ctime
t = time()
ctime(t)

output:

Sat Sep 14 21:27:08 2019

These outputs are different because the timestamp returned by ctime() depends on your geographical location.


在一行中捕获多个异常(块除外)

问题:在一行中捕获多个异常(块除外)

我知道我可以做到:

try:
    # do something that may fail
except:
    # do this if ANYTHING goes wrong

我也可以这样做:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreTooShortException:
    # stand on a ladder

但是,如果我想在两个不同的异常中做同样的事情,那么我现在想到的最好的方法就是:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreBeingMeanException:
    # say please

有什么办法可以做这样的事情(因为在两种情况下都要采取的行动是say please):

try:
    # do something that may fail
except IDontLikeYouException, YouAreBeingMeanException:
    # say please

现在,这确实不起作用,因为它与以下语法匹配:

try:
    # do something that may fail
except Exception, e:
    # say please

因此,我捕捉两个截然不同的异常的努力并未完全实现。

有没有办法做到这一点?

I know that I can do:

try:
    # do something that may fail
except:
    # do this if ANYTHING goes wrong

I can also do this:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreTooShortException:
    # stand on a ladder

But if I want to do the same thing inside two different exceptions, the best I can think of right now is to do this:

try:
    # do something that may fail
except IDontLikeYouException:
    # say please
except YouAreBeingMeanException:
    # say please

Is there any way that I can do something like this (since the action to take in both exceptions is to say please):

try:
    # do something that may fail
except IDontLikeYouException, YouAreBeingMeanException:
    # say please

Now this really won’t work, as it matches the syntax for:

try:
    # do something that may fail
except Exception, e:
    # say please

So, my effort to catch the two distinct exceptions doesn’t exactly come through.

Is there a way to do this?


回答 0

Python文档

例如,except子句可以将多个异常命名为带括号的元组。

except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

或者,仅对于Python 2:

except (IDontLikeYouException, YouAreBeingMeanException), e:
    pass

用逗号将变量与变量分开仍然可以在Python 2.6和2.7中使用,但是现在已弃用,并且在Python 3中不起作用。现在您应该使用as

From Python Documentation:

An except clause may name multiple exceptions as a parenthesized tuple, for example

except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

Or, for Python 2 only:

except (IDontLikeYouException, YouAreBeingMeanException), e:
    pass

Separating the exception from the variable with a comma will still work in Python 2.6 and 2.7, but is now deprecated and does not work in Python 3; now you should be using as.


回答 1

如何在一行中捕获多个异常(块除外)

做这个:

try:
    may_raise_specific_errors():
except (SpecificErrorOne, SpecificErrorTwo) as error:
    handle(error) # might log or have some other default behavior...

由于使用了逗号将错误对象分配给名称的较旧语法,因此需要使用括号。该as关键字用于分配。您可以为错误对象使用任何名称,我error个人更喜欢。

最佳实践

要以当前方式并与Python向前兼容的方式执行此操作,您需要使用逗号分隔Exceptions,并用括号将其包裹起来,以区别于早期的语法,后者通过遵循用逗号。

这是一个简单用法的示例:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError): # the parens are necessary
    sys.exit(0)

我仅指定这些异常以避免隐藏错误,如果遇到错误,我希望从中获得完整的堆栈跟踪。

此处记录:https : //docs.python.org/tutorial/errors.html

您可以将异常分配给变量,(e很常见,但是,如果您需要长时间的异常处理,或者您的IDE像我的一样,仅突出显示比这个更大的选择,那么您可能更喜欢使用更冗长的变量。)实例具有args属性。这是一个例子:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError) as err: 
    print(err)
    print(err.args)
    sys.exit(0)

请注意,在Python 3中,结束errexcept块时该对象不在范围内。

不推荐使用

您可能会看到用逗号分配错误的代码。不赞成使用此用法,它是Python 2.5及更早版本中唯一可用的形式,并且如果您希望代码与Python 3向前兼容,则应更新语法以使用新形式:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError), err: # don't do this in Python 2.6+
    print err
    print err.args
    sys.exit(0)

如果在代码库中看到逗号名称分配,并且您正在使用Python 2.5或更高版本,请切换到新的方式来执行此操作,以便在升级时代码保持兼容。

suppress上下文管理器

可接受的答案实际上是最少4行代码:

try:
    do_something()
except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

tryexceptpass线可以与单线处理抑制上下文管理器,可以在Python 3.4

from contextlib import suppress

with suppress(IDontLikeYouException, YouAreBeingMeanException):
     do_something()

因此,当您想pass在某些exceptions情况下使用suppress

How do I catch multiple exceptions in one line (except block)

Do this:

try:
    may_raise_specific_errors():
except (SpecificErrorOne, SpecificErrorTwo) as error:
    handle(error) # might log or have some other default behavior...

The parentheses are required due to older syntax that used the commas to assign the error object to a name. The as keyword is used for the assignment. You can use any name for the error object, I prefer error personally.

Best Practice

To do this in a manner currently and forward compatible with Python, you need to separate the Exceptions with commas and wrap them with parentheses to differentiate from earlier syntax that assigned the exception instance to a variable name by following the Exception type to be caught with a comma.

Here’s an example of simple usage:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError): # the parens are necessary
    sys.exit(0)

I’m specifying only these exceptions to avoid hiding bugs, which if I encounter I expect the full stack trace from.

This is documented here: https://docs.python.org/tutorial/errors.html

You can assign the exception to a variable, (e is common, but you might prefer a more verbose variable if you have long exception handling or your IDE only highlights selections larger than that, as mine does.) The instance has an args attribute. Here is an example:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError) as err: 
    print(err)
    print(err.args)
    sys.exit(0)

Note that in Python 3, the err object falls out of scope when the except block is concluded.

Deprecated

You may see code that assigns the error with a comma. This usage, the only form available in Python 2.5 and earlier, is deprecated, and if you wish your code to be forward compatible in Python 3, you should update the syntax to use the new form:

import sys

try:
    mainstuff()
except (KeyboardInterrupt, EOFError), err: # don't do this in Python 2.6+
    print err
    print err.args
    sys.exit(0)

If you see the comma name assignment in your codebase, and you’re using Python 2.5 or higher, switch to the new way of doing it so your code remains compatible when you upgrade.

The suppress context manager

The accepted answer is really 4 lines of code, minimum:

try:
    do_something()
except (IDontLikeYouException, YouAreBeingMeanException) as e:
    pass

The try, except, pass lines can be handled in a single line with the suppress context manager, available in Python 3.4:

from contextlib import suppress

with suppress(IDontLikeYouException, YouAreBeingMeanException):
     do_something()

So when you want to pass on certain exceptions, use suppress.


回答 2

Python文档-> 8.3处理异常

一条try语句可能具有多个except子句,以指定不同异常的处理程序。最多将执行一个处理程序。处理程序仅处理在相应的try子句中发生的异常,而不处理同一try语句的其他处理程序中的异常。exclude子句可以将多个异常命名为带括号的元组,例如:

except (RuntimeError, TypeError, NameError):
    pass

请注意,必须在该元组周围加上括号,因为除了ValueError, e:用于except ValueError as e:现代Python中通常编写的语法(如下所述)外,其他语法都是这样。为了向后兼容,仍旧支持旧语法。这意味着except RuntimeError, TypeError不等于 except (RuntimeError, TypeError):except RuntimeError as TypeError:不是您想要的。

From Python documentation -> 8.3 Handling Exceptions:

A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler will be executed. Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. An except clause may name multiple exceptions as a parenthesized tuple, for example:

except (RuntimeError, TypeError, NameError):
    pass

Note that the parentheses around this tuple are required, because except ValueError, e: was the syntax used for what is normally written as except ValueError as e: in modern Python (described below). The old syntax is still supported for backwards compatibility. This means except RuntimeError, TypeError is not equivalent to except (RuntimeError, TypeError): but to except RuntimeError as TypeError: which is not what you want.


回答 3

如果您经常使用大量异常,则可以预定义一个元组,因此不必多次重新键入它们。

#This example code is a technique I use in a library that connects with websites to gather data

ConnectErrs  = (URLError, SSLError, SocketTimeoutError, BadStatusLine, ConnectionResetError)

def connect(url, data):
    #do connection and return some data
    return(received_data)

def some_function(var_a, var_b, ...):
    try: o = connect(url, data)
    except ConnectErrs as e:
        #do the recovery stuff
    blah #do normal stuff you would do if no exception occurred

笔记:

  1. 如果您还需要捕获除预定义元组中的异常以外的其他异常,则需要定义另一个except块。

  2. 如果您不能忍受全局变量,请在main()中定义它,并在需要的地方传递它…

If you frequently use a large number of exceptions, you can pre-define a tuple, so you don’t have to re-type them many times.

#This example code is a technique I use in a library that connects with websites to gather data

ConnectErrs  = (URLError, SSLError, SocketTimeoutError, BadStatusLine, ConnectionResetError)

def connect(url, data):
    #do connection and return some data
    return(received_data)

def some_function(var_a, var_b, ...):
    try: o = connect(url, data)
    except ConnectErrs as e:
        #do the recovery stuff
    blah #do normal stuff you would do if no exception occurred

NOTES:

  1. If you, also, need to catch other exceptions than those in the pre-defined tuple, you will need to define another except block.

  2. If you just cannot tolerate a global variable, define it in main() and pass it around where needed…


回答 4

一种方法是..

try:
   You do your operations here;
   ......................
except(Exception1[, Exception2[,...ExceptionN]]]):
   If there is any exception from the given exception list, 
   then execute this block.
   ......................
else:
   If there is no exception then execute this block. 

另一种方法是创建一种方法,该方法执行由except块执行的任务,并在except您编写的所有块中调用它。

try:
   You do your operations here;
   ......................
except Exception1:
    functionname(parameterList)
except Exception2:
    functionname(parameterList)
except Exception3:
    functionname(parameterList)
else:
   If there is no exception then execute this block. 

def functionname( parameters ):
   //your task..
   return [expression]

我知道第二种方法并不是做到这一点的最佳方法,但我只是在说明一些实现此目的的方法。

One of the way to do this is..

try:
   You do your operations here;
   ......................
except(Exception1[, Exception2[,...ExceptionN]]]):
   If there is any exception from the given exception list, 
   then execute this block.
   ......................
else:
   If there is no exception then execute this block. 

and another way is to create method which performs task executed by except block and call it through all of the except block that you write..

try:
   You do your operations here;
   ......................
except Exception1:
    functionname(parameterList)
except Exception2:
    functionname(parameterList)
except Exception3:
    functionname(parameterList)
else:
   If there is no exception then execute this block. 

def functionname( parameters ):
   //your task..
   return [expression]

I know that second one is not the best way to do this, but i’m just showing number of ways to do this thing.


如何制作功能装饰器链?

问题:如何制作功能装饰器链?

如何在Python中制作两个装饰器,以完成以下工作?

@makebold
@makeitalic
def say():
   return "Hello"

…应返回:

"<b><i>Hello</i></b>"

我并不是想HTML在实际的应用程序中采用这种方式-只是想了解装饰器和装饰器链接的工作方式。

How can I make two decorators in Python that would do the following?

@makebold
@makeitalic
def say():
   return "Hello"

…which should return:

"<b><i>Hello</i></b>"

I’m not trying to make HTML this way in a real application – just trying to understand how decorators and decorator chaining works.


回答 0

查看文档,以了解装饰器如何工作。这是您要求的:

from functools import wraps

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<b>" + fn(*args, **kwargs) + "</b>"
    return wrapped

def makeitalic(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<i>" + fn(*args, **kwargs) + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

@makebold
@makeitalic
def log(s):
    return s

print hello()        # returns "<b><i>hello world</i></b>"
print hello.__name__ # with functools.wraps() this returns "hello"
print log('hello')   # returns "<b><i>hello</i></b>"

Check out the documentation to see how decorators work. Here is what you asked for:

from functools import wraps

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<b>" + fn(*args, **kwargs) + "</b>"
    return wrapped

def makeitalic(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<i>" + fn(*args, **kwargs) + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

@makebold
@makeitalic
def log(s):
    return s

print hello()        # returns "<b><i>hello world</i></b>"
print hello.__name__ # with functools.wraps() this returns "hello"
print log('hello')   # returns "<b><i>hello</i></b>"

回答 1

如果您不做详细解释,请参阅保罗·贝尔甘蒂诺(Paolo Bergantino)的回答

装饰基础

Python的函数是对象

要了解装饰器,您必须首先了解函数是Python中的对象。这具有重要的后果。让我们来看一个简单的例子:

def shout(word="yes"):
    return word.capitalize()+"!"

print(shout())
# outputs : 'Yes!'

# As an object, you can assign the function to a variable like any other object 
scream = shout

# Notice we don't use parentheses: we are not calling the function,
# we are putting the function "shout" into the variable "scream".
# It means you can then call "shout" from "scream":

print(scream())
# outputs : 'Yes!'

# More than that, it means you can remove the old name 'shout',
# and the function will still be accessible from 'scream'

del shout
try:
    print(shout())
except NameError as e:
    print(e)
    #outputs: "name 'shout' is not defined"

print(scream())
# outputs: 'Yes!'

请记住这一点。我们很快会回头再说。

Python函数的另一个有趣特性是可以在另一个函数中定义它们!

def talk():

    # You can define a function on the fly in "talk" ...
    def whisper(word="yes"):
        return word.lower()+"..."

    # ... and use it right away!
    print(whisper())

# You call "talk", that defines "whisper" EVERY TIME you call it, then
# "whisper" is called in "talk". 
talk()
# outputs: 
# "yes..."

# But "whisper" DOES NOT EXIST outside "talk":

try:
    print(whisper())
except NameError as e:
    print(e)
    #outputs : "name 'whisper' is not defined"*
    #Python's functions are objects

功能参考

好吧,还在吗?现在有趣的部分…

您已经看到函数是对象。因此,功能:

  • 可以分配给变量
  • 可以在另一个函数中定义

这意味着一个功能可以return另一个功能

def getTalk(kind="shout"):

    # We define functions on the fly
    def shout(word="yes"):
        return word.capitalize()+"!"

    def whisper(word="yes") :
        return word.lower()+"...";

    # Then we return one of them
    if kind == "shout":
        # We don't use "()", we are not calling the function,
        # we are returning the function object
        return shout  
    else:
        return whisper

# How do you use this strange beast?

# Get the function and assign it to a variable
talk = getTalk()      

# You can see that "talk" is here a function object:
print(talk)
#outputs : <function shout at 0xb7ea817c>

# The object is the one returned by the function:
print(talk())
#outputs : Yes!

# And you can even use it directly if you feel wild:
print(getTalk("whisper")())
#outputs : yes...

还有更多!

如果可以return使用函数,则可以将其中一个作为参数传递:

def doSomethingBefore(func): 
    print("I do something before then I call the function you gave me")
    print(func())

doSomethingBefore(scream)
#outputs: 
#I do something before then I call the function you gave me
#Yes!

好吧,您只需具备了解装饰器所需的一切。您会看到,装饰器是“包装器”,这意味着它们使您可以在装饰函数之前和之后执行代码,而无需修改函数本身。

手工装饰

您将如何手动进行操作:

# A decorator is a function that expects ANOTHER function as parameter
def my_shiny_new_decorator(a_function_to_decorate):

    # Inside, the decorator defines a function on the fly: the wrapper.
    # This function is going to be wrapped around the original function
    # so it can execute code before and after it.
    def the_wrapper_around_the_original_function():

        # Put here the code you want to be executed BEFORE the original function is called
        print("Before the function runs")

        # Call the function here (using parentheses)
        a_function_to_decorate()

        # Put here the code you want to be executed AFTER the original function is called
        print("After the function runs")

    # At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED.
    # We return the wrapper function we have just created.
    # The wrapper contains the function and the code to execute before and after. It’s ready to use!
    return the_wrapper_around_the_original_function

# Now imagine you create a function you don't want to ever touch again.
def a_stand_alone_function():
    print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function() 
#outputs: I am a stand alone function, don't you dare modify me

# Well, you can decorate it to extend its behavior.
# Just pass it to the decorator, it will wrap it dynamically in 
# any code you want and return you a new function ready to be used:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

现在,您可能希望每次调用a_stand_alone_functiona_stand_alone_function_decorated都调用它。这很简单,只需a_stand_alone_function用以下方法返回的函数覆盖my_shiny_new_decorator

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

# That’s EXACTLY what decorators do!

装饰者神秘化

上一个使用装饰器语法的示例:

@my_shiny_new_decorator
def another_stand_alone_function():
    print("Leave me alone")

another_stand_alone_function()  
#outputs:  
#Before the function runs
#Leave me alone
#After the function runs

是的,仅此而已。@decorator只是以下方面的捷径:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

装饰器只是装饰器设计模式的pythonic变体。Python中嵌入了几种经典的设计模式来简化开发(例如迭代器)。

当然,您可以积累装饰器:

def bread(func):
    def wrapper():
        print("</''''''\>")
        func()
        print("<\______/>")
    return wrapper

def ingredients(func):
    def wrapper():
        print("#tomatoes#")
        func()
        print("~salad~")
    return wrapper

def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

使用Python装饰器语法:

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

您设置装饰器重要事项的顺序:

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print(food)

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~

现在:回答问题…

作为结论,您可以轻松地看到如何回答该问题:

# The decorator to make it bold
def makebold(fn):
    # The new function the decorator returns
    def wrapper():
        # Insertion of some code before and after
        return "<b>" + fn() + "</b>"
    return wrapper

# The decorator to make it italic
def makeitalic(fn):
    # The new function the decorator returns
    def wrapper():
        # Insertion of some code before and after
        return "<i>" + fn() + "</i>"
    return wrapper

@makebold
@makeitalic
def say():
    return "hello"

print(say())
#outputs: <b><i>hello</i></b>

# This is the exact equivalent to 
def say():
    return "hello"
say = makebold(makeitalic(say))

print(say())
#outputs: <b><i>hello</i></b>

现在,您可以放开心心,或者多动脑筋,看看装饰器的高级用法。


将装饰者提升到一个新的水平

将参数传递给装饰函数

# It’s not black magic, you just have to let the wrapper 
# pass the argument:

def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
        print("I got args! Look: {0}, {1}".format(arg1, arg2))
        function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments

# Since when you are calling the function returned by the decorator, you are
# calling the wrapper, passing arguments to the wrapper will let it pass them to 
# the decorated function

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print("My name is {0} {1}".format(first_name, last_name))

print_full_name("Peter", "Venkman")
# outputs:
#I got args! Look: Peter Venkman
#My name is Peter Venkman

装饰方式

关于Python的一件事是方法和函数实际上是相同的。唯一的区别是方法期望它们的第一个参数是对当前对象(self)的引用。

这意味着您可以以相同的方式为方法构建装饰器!只要记住要self考虑:

def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        lie = lie - 3 # very friendly, decrease age even more :-)
        return method_to_decorate(self, lie)
    return wrapper


class Lucy(object):

    def __init__(self):
        self.age = 32

    @method_friendly_decorator
    def sayYourAge(self, lie):
        print("I am {0}, what did you think?".format(self.age + lie))

l = Lucy()
l.sayYourAge(-3)
#outputs: I am 26, what did you think?

如果要制作通用装饰器(无论参数如何,您都可以将其应用于任何函数或方法),则只需使用*args, **kwargs

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # The wrapper accepts any arguments
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print("Do I have args?:")
        print(args)
        print(kwargs)
        # Then you unpack the arguments, here *args, **kwargs
        # If you are not familiar with unpacking, check:
        # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
        function_to_decorate(*args, **kwargs)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("Python is cool, no argument here.")

function_with_no_argument()
#outputs
#Do I have args?:
#()
#{}
#Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
#outputs
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3 

@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
    print("Do {0}, {1} and {2} like platypus? {3}".format(a, b, c, platypus))

function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
#outputs
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!

class Mary(object):

    def __init__(self):
        self.age = 31

    @a_decorator_passing_arbitrary_arguments
    def sayYourAge(self, lie=-3): # You can now add a default value
        print("I am {0}, what did you think?".format(self.age + lie))

m = Mary()
m.sayYourAge()
#outputs
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?

将参数传递给装饰器

太好了,关于将参数传递给装饰器本身,您会说什么?

因为装饰器必须接受一个函数作为参数,所以这可能会有些扭曲。因此,您不能将装饰函数的参数直接传递给装饰器。

在寻求解决方案之前,让我们写一些提醒:

# Decorators are ORDINARY functions
def my_decorator(func):
    print("I am an ordinary function")
    def wrapper():
        print("I am function returned by the decorator")
        func()
    return wrapper

# Therefore, you can call it without any "@"

def lazy_function():
    print("zzzzzzzz")

decorated_function = my_decorator(lazy_function)
#outputs: I am an ordinary function

# It outputs "I am an ordinary function", because that’s just what you do:
# calling a function. Nothing magic.

@my_decorator
def lazy_function():
    print("zzzzzzzz")

#outputs: I am an ordinary function

完全一样 “ my_decorator”被调用。因此,当您使用时@my_decorator,您要告诉Python调用函数“由变量“ my_decorator” 标记”。

这个很重要!你给的标签可以直接指向decorator- 与否

让我们变得邪恶。☺

def decorator_maker():

    print("I make decorators! I am executed only once: "
          "when you make me create a decorator.")

    def my_decorator(func):

        print("I am a decorator! I am executed only when you decorate a function.")

        def wrapped():
            print("I am the wrapper around the decorated function. "
                  "I am called when you call the decorated function. "
                  "As the wrapper, I return the RESULT of the decorated function.")
            return func()

        print("As the decorator, I return the wrapped function.")

        return wrapped

    print("As a decorator maker, I return a decorator")
    return my_decorator

# Let’s create a decorator. It’s just a new function after all.
new_decorator = decorator_maker()       
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

# Then we decorate the function

def decorated_function():
    print("I am the decorated function.")

decorated_function = new_decorator(decorated_function)
#outputs:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function

# Let’s call the function:
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

毫不奇怪。

让我们做完全一样的事情,但是跳过所有讨厌的中间变量:

def decorated_function():
    print("I am the decorated function.")
decorated_function = decorator_maker()(decorated_function)
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# Finally:
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

让我们把它变得更短

@decorator_maker()
def decorated_function():
    print("I am the decorated function.")
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

#Eventually: 
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

嘿,你看到了吗?我们使用了带有“ @”语法的函数调用!:-)

因此,回到带有参数的装饰器。如果我们可以使用函数即时生成装饰器,则可以将参数传递给该函数,对吗?

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):

    print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

    def my_decorator(func):
        # The ability to pass arguments here is a gift from closures.
        # If you are not comfortable with closures, you can assume it’s ok,
        # or read: /programming/13857/can-you-explain-closures-as-they-relate-to-python
        print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

        # Don't confuse decorator arguments and function arguments!
        def wrapped(function_arg1, function_arg2) :
            print("I am the wrapper around the decorated function.\n"
                  "I can access all the variables\n"
                  "\t- from the decorator: {0} {1}\n"
                  "\t- from the function call: {2} {3}\n"
                  "Then I can pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,
                          function_arg1, function_arg2))
            return func(function_arg1, function_arg2)

        return wrapped

    return my_decorator

@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
    print("I am the decorated function and only knows about my arguments: {0}"
           " {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Sheldon 
#   - from the function call: Rajesh Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard

它是:带参数的装饰器。可以将参数设置为变量:

c1 = "Penny"
c2 = "Leslie"

@decorator_maker_with_arguments("Leonard", c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
    print("I am the decorated function and only knows about my arguments:"
           " {0} {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments(c2, "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Penny 
#   - from the function call: Leslie Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only know about my arguments: Leslie Howard

如您所见,可以使用此技巧像其他任何函数一样将参数传递给装饰器。您甚至可以*args, **kwargs根据需要使用。但是请记住,装饰器被调用一次。就在Python导入脚本时。之后,您将无法动态设置参数。当您执行“ import x”时,该函数已经被修饰,因此您无法进行任何更改。


练习:装饰装饰器

好的,作为奖励,我将向您提供一个片段,以使任何装饰器通常接受任何参数。毕竟,为了接受参数,我们使用了另一个函数来创建装饰器。

我们包装了装饰器。

我们最近看到的还有其他包装功能吗?

哦,是的,装饰品!

让我们玩得开心,为装饰者写一个装饰者:

def decorator_with_args(decorator_to_enhance):
    """ 
    This function is supposed to be used as a decorator.
    It must decorate an other function, that is intended to be used as a decorator.
    Take a cup of coffee.
    It will allow any decorator to accept an arbitrary number of arguments,
    saving you the headache to remember how to do that every time.
    """

    # We use the same trick we did to pass arguments
    def decorator_maker(*args, **kwargs):

        # We create on the fly a decorator that accepts only a function
        # but keeps the passed arguments from the maker.
        def decorator_wrapper(func):

            # We return the result of the original decorator, which, after all, 
            # IS JUST AN ORDINARY FUNCTION (which returns a function).
            # Only pitfall: the decorator must have this specific signature or it won't work:
            return decorator_to_enhance(func, *args, **kwargs)

        return decorator_wrapper

    return decorator_maker

可以如下使用:

# You create the function you will use as a decorator. And stick a decorator on it :-)
# Don't forget, the signature is "decorator(func, *args, **kwargs)"
@decorator_with_args 
def decorated_decorator(func, *args, **kwargs): 
    def wrapper(function_arg1, function_arg2):
        print("Decorated with {0} {1}".format(args, kwargs))
        return func(function_arg1, function_arg2)
    return wrapper

# Then you decorate the functions you wish with your brand new decorated decorator.

@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
    print("Hello {0} {1}".format(function_arg1, function_arg2))

decorated_function("Universe and", "everything")
#outputs:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

# Whoooot!

我知道,上一次您有这种感觉时,是在听一个人说:“了解递归之前,您必须先了解递归”。但是现在,您是否对掌握这一点感到满意?


最佳做法:装饰

  • 装饰器是在Python 2.4中引入的,因此请确保您的代码将在> = 2.4上运行。
  • 装饰器使函数调用变慢。记住这一点。
  • 您不能取消装饰功能。(有一些技巧可以创建可以删除的装饰器,但是没有人使用它们。)因此,一旦装饰了函数,就对所有代码进行装饰。
  • 装饰器包装函数,这会使它们难以调试。(这在Python> = 2.5时会更好;请参见下文。)

functools模块是在Python 2.5中引入的。它包括功能functools.wraps(),该将修饰后的函数的名称,模块和文档字符串复制到其包装器中。

(有趣的事实:functools.wraps()是一个装饰!☺)

# For debugging, the stacktrace prints you the function __name__
def foo():
    print("foo")

print(foo.__name__)
#outputs: foo

# With a decorator, it gets messy    
def bar(func):
    def wrapper():
        print("bar")
        return func()
    return wrapper

@bar
def foo():
    print("foo")

print(foo.__name__)
#outputs: wrapper

# "functools" can help for that

import functools

def bar(func):
    # We say that "wrapper", is wrapping "func"
    # and the magic begins
    @functools.wraps(func)
    def wrapper():
        print("bar")
        return func()
    return wrapper

@bar
def foo():
    print("foo")

print(foo.__name__)
#outputs: foo

装饰器如何发挥作用?

现在有个大问题:我可以使用装饰器做什么?

看起来很酷而且功能强大,但是一个实际的例子将是很好的。好吧,这里有1000种可能性。经典用法是从外部库扩展功能行为(您不能对其进行修改),或者用于调试(您不希望对其进行修改,因为它是临时的)。

您可以使用它们以DRY的方式扩展多个功能,如下所示:

def benchmark(func):
    """
    A decorator that prints the time a function takes
    to execute.
    """
    import time
    def wrapper(*args, **kwargs):
        t = time.clock()
        res = func(*args, **kwargs)
        print("{0} {1}".format(func.__name__, time.clock()-t))
        return res
    return wrapper


def logging(func):
    """
    A decorator that logs the activity of the script.
    (it actually just prints it, but it could be logging!)
    """
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print("{0} {1} {2}".format(func.__name__, args, kwargs))
        return res
    return wrapper


def counter(func):
    """
    A decorator that counts and prints the number of times a function has been executed
    """
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print("{0} has been used: {1}x".format(func.__name__, wrapper.count))
        return res
    wrapper.count = 0
    return wrapper

@counter
@benchmark
@logging
def reverse_string(string):
    return str(reversed(string))

print(reverse_string("Able was I ere I saw Elba"))
print(reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!"))

#outputs:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x 
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A

当然,装饰器的好处是,您几乎可以在几乎所有内容上使用它们而无需重写。干,我说:

@counter
@benchmark
@logging
def get_random_futurama_quote():
    from urllib import urlopen
    result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read()
    try:
        value = result.split("<br><b><hr><br>")[1].split("<br><br><hr>")[0]
        return value.strip()
    except:
        return "No, I'm ... doesn't!"


print(get_random_futurama_quote())
print(get_random_futurama_quote())

#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!

Python本身提供了一些装饰:propertystaticmethod,等。

  • Django使用装饰器来管理缓存和查看权限。
  • 扭曲到伪造的内联异步函数调用。

这真的是一个大操场。

If you are not into long explanations, see Paolo Bergantino’s answer.

Decorator Basics

Python’s functions are objects

To understand decorators, you must first understand that functions are objects in Python. This has important consequences. Let’s see why with a simple example :

def shout(word="yes"):
    return word.capitalize()+"!"

print(shout())
# outputs : 'Yes!'

# As an object, you can assign the function to a variable like any other object 
scream = shout

# Notice we don't use parentheses: we are not calling the function,
# we are putting the function "shout" into the variable "scream".
# It means you can then call "shout" from "scream":

print(scream())
# outputs : 'Yes!'

# More than that, it means you can remove the old name 'shout',
# and the function will still be accessible from 'scream'

del shout
try:
    print(shout())
except NameError as e:
    print(e)
    #outputs: "name 'shout' is not defined"

print(scream())
# outputs: 'Yes!'

Keep this in mind. We’ll circle back to it shortly.

Another interesting property of Python functions is they can be defined inside another function!

def talk():

    # You can define a function on the fly in "talk" ...
    def whisper(word="yes"):
        return word.lower()+"..."

    # ... and use it right away!
    print(whisper())

# You call "talk", that defines "whisper" EVERY TIME you call it, then
# "whisper" is called in "talk". 
talk()
# outputs: 
# "yes..."

# But "whisper" DOES NOT EXIST outside "talk":

try:
    print(whisper())
except NameError as e:
    print(e)
    #outputs : "name 'whisper' is not defined"*
    #Python's functions are objects

Functions references

Okay, still here? Now the fun part…

You’ve seen that functions are objects. Therefore, functions:

  • can be assigned to a variable
  • can be defined in another function

That means that a function can return another function.

def getTalk(kind="shout"):

    # We define functions on the fly
    def shout(word="yes"):
        return word.capitalize()+"!"

    def whisper(word="yes") :
        return word.lower()+"...";

    # Then we return one of them
    if kind == "shout":
        # We don't use "()", we are not calling the function,
        # we are returning the function object
        return shout  
    else:
        return whisper

# How do you use this strange beast?

# Get the function and assign it to a variable
talk = getTalk()      

# You can see that "talk" is here a function object:
print(talk)
#outputs : <function shout at 0xb7ea817c>

# The object is the one returned by the function:
print(talk())
#outputs : Yes!

# And you can even use it directly if you feel wild:
print(getTalk("whisper")())
#outputs : yes...

There’s more!

If you can return a function, you can pass one as a parameter:

def doSomethingBefore(func): 
    print("I do something before then I call the function you gave me")
    print(func())

doSomethingBefore(scream)
#outputs: 
#I do something before then I call the function you gave me
#Yes!

Well, you just have everything needed to understand decorators. You see, decorators are “wrappers”, which means that they let you execute code before and after the function they decorate without modifying the function itself.

Handcrafted decorators

How you’d do it manually:

# A decorator is a function that expects ANOTHER function as parameter
def my_shiny_new_decorator(a_function_to_decorate):

    # Inside, the decorator defines a function on the fly: the wrapper.
    # This function is going to be wrapped around the original function
    # so it can execute code before and after it.
    def the_wrapper_around_the_original_function():

        # Put here the code you want to be executed BEFORE the original function is called
        print("Before the function runs")

        # Call the function here (using parentheses)
        a_function_to_decorate()

        # Put here the code you want to be executed AFTER the original function is called
        print("After the function runs")

    # At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED.
    # We return the wrapper function we have just created.
    # The wrapper contains the function and the code to execute before and after. It’s ready to use!
    return the_wrapper_around_the_original_function

# Now imagine you create a function you don't want to ever touch again.
def a_stand_alone_function():
    print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function() 
#outputs: I am a stand alone function, don't you dare modify me

# Well, you can decorate it to extend its behavior.
# Just pass it to the decorator, it will wrap it dynamically in 
# any code you want and return you a new function ready to be used:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

Now, you probably want that every time you call a_stand_alone_function, a_stand_alone_function_decorated is called instead. That’s easy, just overwrite a_stand_alone_function with the function returned by my_shiny_new_decorator:

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

# That’s EXACTLY what decorators do!

Decorators demystified

The previous example, using the decorator syntax:

@my_shiny_new_decorator
def another_stand_alone_function():
    print("Leave me alone")

another_stand_alone_function()  
#outputs:  
#Before the function runs
#Leave me alone
#After the function runs

Yes, that’s all, it’s that simple. @decorator is just a shortcut to:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

Decorators are just a pythonic variant of the decorator design pattern. There are several classic design patterns embedded in Python to ease development (like iterators).

Of course, you can accumulate decorators:

def bread(func):
    def wrapper():
        print("</''''''\>")
        func()
        print("<\______/>")
    return wrapper

def ingredients(func):
    def wrapper():
        print("#tomatoes#")
        func()
        print("~salad~")
    return wrapper

def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

Using the Python decorator syntax:

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

The order you set the decorators MATTERS:

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print(food)

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~

Now: to answer the question…

As a conclusion, you can easily see how to answer the question:

# The decorator to make it bold
def makebold(fn):
    # The new function the decorator returns
    def wrapper():
        # Insertion of some code before and after
        return "<b>" + fn() + "</b>"
    return wrapper

# The decorator to make it italic
def makeitalic(fn):
    # The new function the decorator returns
    def wrapper():
        # Insertion of some code before and after
        return "<i>" + fn() + "</i>"
    return wrapper

@makebold
@makeitalic
def say():
    return "hello"

print(say())
#outputs: <b><i>hello</i></b>

# This is the exact equivalent to 
def say():
    return "hello"
say = makebold(makeitalic(say))

print(say())
#outputs: <b><i>hello</i></b>

You can now just leave happy, or burn your brain a little bit more and see advanced uses of decorators.


Taking decorators to the next level

Passing arguments to the decorated function

# It’s not black magic, you just have to let the wrapper 
# pass the argument:

def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
        print("I got args! Look: {0}, {1}".format(arg1, arg2))
        function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments

# Since when you are calling the function returned by the decorator, you are
# calling the wrapper, passing arguments to the wrapper will let it pass them to 
# the decorated function

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print("My name is {0} {1}".format(first_name, last_name))

print_full_name("Peter", "Venkman")
# outputs:
#I got args! Look: Peter Venkman
#My name is Peter Venkman

Decorating methods

One nifty thing about Python is that methods and functions are really the same. The only difference is that methods expect that their first argument is a reference to the current object (self).

That means you can build a decorator for methods the same way! Just remember to take self into consideration:

def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        lie = lie - 3 # very friendly, decrease age even more :-)
        return method_to_decorate(self, lie)
    return wrapper


class Lucy(object):

    def __init__(self):
        self.age = 32

    @method_friendly_decorator
    def sayYourAge(self, lie):
        print("I am {0}, what did you think?".format(self.age + lie))

l = Lucy()
l.sayYourAge(-3)
#outputs: I am 26, what did you think?

If you’re making general-purpose decorator–one you’ll apply to any function or method, no matter its arguments–then just use *args, **kwargs:

def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # The wrapper accepts any arguments
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print("Do I have args?:")
        print(args)
        print(kwargs)
        # Then you unpack the arguments, here *args, **kwargs
        # If you are not familiar with unpacking, check:
        # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
        function_to_decorate(*args, **kwargs)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print("Python is cool, no argument here.")

function_with_no_argument()
#outputs
#Do I have args?:
#()
#{}
#Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print(a, b, c)

function_with_arguments(1,2,3)
#outputs
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3 

@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
    print("Do {0}, {1} and {2} like platypus? {3}".format(a, b, c, platypus))

function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
#outputs
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!

class Mary(object):

    def __init__(self):
        self.age = 31

    @a_decorator_passing_arbitrary_arguments
    def sayYourAge(self, lie=-3): # You can now add a default value
        print("I am {0}, what did you think?".format(self.age + lie))

m = Mary()
m.sayYourAge()
#outputs
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?

Passing arguments to the decorator

Great, now what would you say about passing arguments to the decorator itself?

This can get somewhat twisted, since a decorator must accept a function as an argument. Therefore, you cannot pass the decorated function’s arguments directly to the decorator.

Before rushing to the solution, let’s write a little reminder:

# Decorators are ORDINARY functions
def my_decorator(func):
    print("I am an ordinary function")
    def wrapper():
        print("I am function returned by the decorator")
        func()
    return wrapper

# Therefore, you can call it without any "@"

def lazy_function():
    print("zzzzzzzz")

decorated_function = my_decorator(lazy_function)
#outputs: I am an ordinary function

# It outputs "I am an ordinary function", because that’s just what you do:
# calling a function. Nothing magic.

@my_decorator
def lazy_function():
    print("zzzzzzzz")

#outputs: I am an ordinary function

It’s exactly the same. “my_decorator” is called. So when you @my_decorator, you are telling Python to call the function ‘labelled by the variable “my_decorator“‘.

This is important! The label you give can point directly to the decorator—or not.

Let’s get evil. ☺

def decorator_maker():

    print("I make decorators! I am executed only once: "
          "when you make me create a decorator.")

    def my_decorator(func):

        print("I am a decorator! I am executed only when you decorate a function.")

        def wrapped():
            print("I am the wrapper around the decorated function. "
                  "I am called when you call the decorated function. "
                  "As the wrapper, I return the RESULT of the decorated function.")
            return func()

        print("As the decorator, I return the wrapped function.")

        return wrapped

    print("As a decorator maker, I return a decorator")
    return my_decorator

# Let’s create a decorator. It’s just a new function after all.
new_decorator = decorator_maker()       
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

# Then we decorate the function

def decorated_function():
    print("I am the decorated function.")

decorated_function = new_decorator(decorated_function)
#outputs:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function

# Let’s call the function:
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

No surprise here.

Let’s do EXACTLY the same thing, but skip all the pesky intermediate variables:

def decorated_function():
    print("I am the decorated function.")
decorated_function = decorator_maker()(decorated_function)
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# Finally:
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

Let’s make it even shorter:

@decorator_maker()
def decorated_function():
    print("I am the decorated function.")
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

#Eventually: 
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.

Hey, did you see that? We used a function call with the “@” syntax! :-)

So, back to decorators with arguments. If we can use functions to generate the decorator on the fly, we can pass arguments to that function, right?

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):

    print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

    def my_decorator(func):
        # The ability to pass arguments here is a gift from closures.
        # If you are not comfortable with closures, you can assume it’s ok,
        # or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
        print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))

        # Don't confuse decorator arguments and function arguments!
        def wrapped(function_arg1, function_arg2) :
            print("I am the wrapper around the decorated function.\n"
                  "I can access all the variables\n"
                  "\t- from the decorator: {0} {1}\n"
                  "\t- from the function call: {2} {3}\n"
                  "Then I can pass them to the decorated function"
                  .format(decorator_arg1, decorator_arg2,
                          function_arg1, function_arg2))
            return func(function_arg1, function_arg2)

        return wrapped

    return my_decorator

@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
    print("I am the decorated function and only knows about my arguments: {0}"
           " {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Sheldon 
#   - from the function call: Rajesh Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard

Here it is: a decorator with arguments. Arguments can be set as variable:

c1 = "Penny"
c2 = "Leslie"

@decorator_maker_with_arguments("Leonard", c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
    print("I am the decorated function and only knows about my arguments:"
           " {0} {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments(c2, "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Penny 
#   - from the function call: Leslie Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only know about my arguments: Leslie Howard

As you can see, you can pass arguments to the decorator like any function using this trick. You can even use *args, **kwargs if you wish. But remember decorators are called only once. Just when Python imports the script. You can’t dynamically set the arguments afterwards. When you do “import x”, the function is already decorated, so you can’t change anything.


Let’s practice: decorating a decorator

Okay, as a bonus, I’ll give you a snippet to make any decorator accept generically any argument. After all, in order to accept arguments, we created our decorator using another function.

We wrapped the decorator.

Anything else we saw recently that wrapped function?

Oh yes, decorators!

Let’s have some fun and write a decorator for the decorators:

def decorator_with_args(decorator_to_enhance):
    """ 
    This function is supposed to be used as a decorator.
    It must decorate an other function, that is intended to be used as a decorator.
    Take a cup of coffee.
    It will allow any decorator to accept an arbitrary number of arguments,
    saving you the headache to remember how to do that every time.
    """

    # We use the same trick we did to pass arguments
    def decorator_maker(*args, **kwargs):

        # We create on the fly a decorator that accepts only a function
        # but keeps the passed arguments from the maker.
        def decorator_wrapper(func):

            # We return the result of the original decorator, which, after all, 
            # IS JUST AN ORDINARY FUNCTION (which returns a function).
            # Only pitfall: the decorator must have this specific signature or it won't work:
            return decorator_to_enhance(func, *args, **kwargs)

        return decorator_wrapper

    return decorator_maker

It can be used as follows:

# You create the function you will use as a decorator. And stick a decorator on it :-)
# Don't forget, the signature is "decorator(func, *args, **kwargs)"
@decorator_with_args 
def decorated_decorator(func, *args, **kwargs): 
    def wrapper(function_arg1, function_arg2):
        print("Decorated with {0} {1}".format(args, kwargs))
        return func(function_arg1, function_arg2)
    return wrapper

# Then you decorate the functions you wish with your brand new decorated decorator.

@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
    print("Hello {0} {1}".format(function_arg1, function_arg2))

decorated_function("Universe and", "everything")
#outputs:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

# Whoooot!

I know, the last time you had this feeling, it was after listening a guy saying: “before understanding recursion, you must first understand recursion”. But now, don’t you feel good about mastering this?


Best practices: decorators

  • Decorators were introduced in Python 2.4, so be sure your code will be run on >= 2.4.
  • Decorators slow down the function call. Keep that in mind.
  • You cannot un-decorate a function. (There are hacks to create decorators that can be removed, but nobody uses them.) So once a function is decorated, it’s decorated for all the code.
  • Decorators wrap functions, which can make them hard to debug. (This gets better from Python >= 2.5; see below.)

The functools module was introduced in Python 2.5. It includes the function functools.wraps(), which copies the name, module, and docstring of the decorated function to its wrapper.

(Fun fact: functools.wraps() is a decorator! ☺)

# For debugging, the stacktrace prints you the function __name__
def foo():
    print("foo")

print(foo.__name__)
#outputs: foo

# With a decorator, it gets messy    
def bar(func):
    def wrapper():
        print("bar")
        return func()
    return wrapper

@bar
def foo():
    print("foo")

print(foo.__name__)
#outputs: wrapper

# "functools" can help for that

import functools

def bar(func):
    # We say that "wrapper", is wrapping "func"
    # and the magic begins
    @functools.wraps(func)
    def wrapper():
        print("bar")
        return func()
    return wrapper

@bar
def foo():
    print("foo")

print(foo.__name__)
#outputs: foo

How can the decorators be useful?

Now the big question: What can I use decorators for?

Seem cool and powerful, but a practical example would be great. Well, there are 1000 possibilities. Classic uses are extending a function behavior from an external lib (you can’t modify it), or for debugging (you don’t want to modify it because it’s temporary).

You can use them to extend several functions in a DRY’s way, like so:

def benchmark(func):
    """
    A decorator that prints the time a function takes
    to execute.
    """
    import time
    def wrapper(*args, **kwargs):
        t = time.clock()
        res = func(*args, **kwargs)
        print("{0} {1}".format(func.__name__, time.clock()-t))
        return res
    return wrapper


def logging(func):
    """
    A decorator that logs the activity of the script.
    (it actually just prints it, but it could be logging!)
    """
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print("{0} {1} {2}".format(func.__name__, args, kwargs))
        return res
    return wrapper


def counter(func):
    """
    A decorator that counts and prints the number of times a function has been executed
    """
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print("{0} has been used: {1}x".format(func.__name__, wrapper.count))
        return res
    wrapper.count = 0
    return wrapper

@counter
@benchmark
@logging
def reverse_string(string):
    return str(reversed(string))

print(reverse_string("Able was I ere I saw Elba"))
print(reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!"))

#outputs:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x 
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A

Of course the good thing with decorators is that you can use them right away on almost anything without rewriting. DRY, I said:

@counter
@benchmark
@logging
def get_random_futurama_quote():
    from urllib import urlopen
    result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read()
    try:
        value = result.split("<br><b><hr><br>")[1].split("<br><br><hr>")[0]
        return value.strip()
    except:
        return "No, I'm ... doesn't!"


print(get_random_futurama_quote())
print(get_random_futurama_quote())

#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!

Python itself provides several decorators: property, staticmethod, etc.

  • Django uses decorators to manage caching and view permissions.
  • Twisted to fake inlining asynchronous functions calls.

This really is a large playground.


回答 2

另外,您可以编写一个工厂函数,该函数返回一个装饰器,该装饰器将装饰后的函数的返回值包装在传递给工厂函数的标记中。例如:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator():
            return '<%(tag)s>%(rv)s</%(tag)s>' % (
                {'tag': tag, 'rv': func()})
        return decorator
    return factory

这使您可以编写:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say():
    return 'hello'

要么

makebold = wrap_in_tag('b')
makeitalic = wrap_in_tag('i')

@makebold
@makeitalic
def say():
    return 'hello'

就个人而言,我会以不同的方式编写装饰器:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator(val):
            return func('<%(tag)s>%(val)s</%(tag)s>' %
                        {'tag': tag, 'val': val})
        return decorator
    return factory

这将生成:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say(val):
    return val
say('hello')

不要忘记装饰器语法是其速记形式的构造:

say = wrap_in_tag('b')(wrap_in_tag('i')(say)))

Alternatively, you could write a factory function which return a decorator which wraps the return value of the decorated function in a tag passed to the factory function. For example:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator():
            return '<%(tag)s>%(rv)s</%(tag)s>' % (
                {'tag': tag, 'rv': func()})
        return decorator
    return factory

This enables you to write:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say():
    return 'hello'

or

makebold = wrap_in_tag('b')
makeitalic = wrap_in_tag('i')

@makebold
@makeitalic
def say():
    return 'hello'

Personally I would have written the decorator somewhat differently:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator(val):
            return func('<%(tag)s>%(val)s</%(tag)s>' %
                        {'tag': tag, 'val': val})
        return decorator
    return factory

which would yield:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say(val):
    return val
say('hello')

Don’t forget the construction for which decorator syntax is a shorthand:

say = wrap_in_tag('b')(wrap_in_tag('i')(say)))

回答 3

看来其他人已经告诉您如何解决问题。希望这可以帮助您了解什么是装饰器。

装饰器只是语法糖。

这个

@decorator
def func():
    ...

扩展到

def func():
    ...
func = decorator(func)

It looks like the other people have already told you how to solve the problem. I hope this will help you understand what decorators are.

Decorators are just syntactical sugar.

This

@decorator
def func():
    ...

expands to

def func():
    ...
func = decorator(func)

回答 4

当然,您也可以从装饰器函数返回lambda:

def makebold(f): 
    return lambda: "<b>" + f() + "</b>"
def makeitalic(f): 
    return lambda: "<i>" + f() + "</i>"

@makebold
@makeitalic
def say():
    return "Hello"

print say()

And of course you can return lambdas as well from a decorator function:

def makebold(f): 
    return lambda: "<b>" + f() + "</b>"
def makeitalic(f): 
    return lambda: "<i>" + f() + "</i>"

@makebold
@makeitalic
def say():
    return "Hello"

print say()

回答 5

Python装饰器为其他功能添加了额外的功能

斜体装饰器可能像

def makeitalic(fn):
    def newFunc():
        return "<i>" + fn() + "</i>"
    return newFunc

请注意,函数是在函数内部定义的。它的主要作用是用新定义的功能替换功能。例如我有这个课

class foo:
    def bar(self):
        print "hi"
    def foobar(self):
        print "hi again"

现在说,我希望两个函数在完成之前和之后都打印“ —”。我可以在每个打印语句之前和之后添加打印“-”。但是因为我不喜欢重复自己,所以我会做一个装饰工

def addDashes(fn): # notice it takes a function as an argument
    def newFunction(self): # define a new function
        print "---"
        fn(self) # call the original function
        print "---"
    return newFunction
    # Return the newly defined function - it will "replace" the original

所以现在我可以将类更改为

class foo:
    @addDashes
    def bar(self):
        print "hi"

    @addDashes
    def foobar(self):
        print "hi again"

有关装饰器的更多信息,请 访问http://www.ibm.com/developerworks/linux/library/l-cpdecor.html。

Python decorators add extra functionality to another function

An italics decorator could be like

def makeitalic(fn):
    def newFunc():
        return "<i>" + fn() + "</i>"
    return newFunc

Note that a function is defined inside a function. What it basically does is replace a function with the newly defined one. For example, I have this class

class foo:
    def bar(self):
        print "hi"
    def foobar(self):
        print "hi again"

Now say, I want both functions to print “—” after and before they are done. I could add a print “—” before and after each print statement. But because I don’t like repeating myself, I will make a decorator

def addDashes(fn): # notice it takes a function as an argument
    def newFunction(self): # define a new function
        print "---"
        fn(self) # call the original function
        print "---"
    return newFunction
    # Return the newly defined function - it will "replace" the original

So now I can change my class to

class foo:
    @addDashes
    def bar(self):
        print "hi"

    @addDashes
    def foobar(self):
        print "hi again"

For more on decorators, check http://www.ibm.com/developerworks/linux/library/l-cpdecor.html


回答 6

可以制作两个单独的装饰器,按您的意愿进行操作,如下所示。请注意*args, **kwargs,在wrapped()函数的声明中使用,该声明支持具有多个参数的修饰函数(对于示例say()函数而言,这并不是必需的,但出于一般性考虑而包括在内)。

出于类似的原因,functools.wraps装饰器用于将包装功能的元属性更改为要装饰的功能的元属性。这使错误消息和嵌入式功能文档(func.__doc__)成为修饰后的函数的错误消息,而不是wrapped()

from functools import wraps

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<b>" + fn(*args, **kwargs) + "</b>"
    return wrapped

def makeitalic(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<i>" + fn(*args, **kwargs) + "</i>"
    return wrapped

@makebold
@makeitalic
def say():
    return 'Hello'

print(say())  # -> <b><i>Hello</i></b>

细化

如您所见,这两个装饰器中有很多重复的代码。鉴于这种相似性,您最好改成一个实际上是装饰器工厂的通用泛型,换句话说,就是制造其他装饰器的装饰器功能。这样,代码重复将更少,并允许遵循DRY原则。

def html_deco(tag):
    def decorator(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            return '<%s>' % tag + fn(*args, **kwargs) + '</%s>' % tag
        return wrapped
    return decorator

@html_deco('b')
@html_deco('i')
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

为了使代码更具可读性,可以为工厂生成的装饰器分配一个更具描述性的名称:

makebold = html_deco('b')
makeitalic = html_deco('i')

@makebold
@makeitalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

甚至像这样组合它们:

makebolditalic = lambda fn: makebold(makeitalic(fn))

@makebolditalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

效率

尽管上面的示例可以完成所有工作,但是当同时应用多个装饰器时,生成的代码会以大量外部函数调用的形式涉及大量开销。这可能无关紧要,具体取决于确切的用法(例如,可能受I / O限制)。

如果装饰函数的速度很重要,则可以通过编写稍有不同的装饰器工厂函数(可一次添加所有标记)来将开销保持在一个额外的函数调用中,因此它可以生成避免发生附加函数调用的代码通过为每个标签使用单独的装饰器。

这需要装饰器本身中的更多代码,但这仅在将其应用于函数定义时才运行,而不是在调用它们本身时才运行。当通过使用lambda如前所述的函数创建更易读的名称时,这也适用。样品:

def multi_html_deco(*tags):
    start_tags, end_tags = [], []
    for tag in tags:
        start_tags.append('<%s>' % tag)
        end_tags.append('</%s>' % tag)
    start_tags = ''.join(start_tags)
    end_tags = ''.join(reversed(end_tags))

    def decorator(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            return start_tags + fn(*args, **kwargs) + end_tags
        return wrapped
    return decorator

makebolditalic = multi_html_deco('b', 'i')

@makebolditalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

You could make two separate decorators that do what you want as illustrated directly below. Note the use of *args, **kwargs in the declaration of the wrapped() function which supports the decorated function having multiple arguments (which isn’t really necessary for the example say() function, but is included for generality).

For similar reasons, the functools.wraps decorator is used to change the meta attributes of the wrapped function to be those of the one being decorated. This makes error messages and embedded function documentation (func.__doc__) be those of the decorated function instead of wrapped()‘s.

from functools import wraps

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<b>" + fn(*args, **kwargs) + "</b>"
    return wrapped

def makeitalic(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return "<i>" + fn(*args, **kwargs) + "</i>"
    return wrapped

@makebold
@makeitalic
def say():
    return 'Hello'

print(say())  # -> <b><i>Hello</i></b>

Refinements

As you can see there’s a lot of duplicate code in these two decorators. Given this similarity it would be better for you to instead make a generic one that was actually a decorator factory—in other words, a decorator function that makes other decorators. That way there would be less code repetition—and allow the DRY principle to be followed.

def html_deco(tag):
    def decorator(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            return '<%s>' % tag + fn(*args, **kwargs) + '</%s>' % tag
        return wrapped
    return decorator

@html_deco('b')
@html_deco('i')
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

To make the code more readable, you can assign a more descriptive name to the factory-generated decorators:

makebold = html_deco('b')
makeitalic = html_deco('i')

@makebold
@makeitalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

or even combine them like this:

makebolditalic = lambda fn: makebold(makeitalic(fn))

@makebolditalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

Efficiency

While the above examples do all work, the code generated involves a fair amount of overhead in the form of extraneous function calls when multiple decorators are applied at once. This may not matter, depending the exact usage (which might be I/O-bound, for instance).

If speed of the decorated function is important, the overhead can be kept to a single extra function call by writing a slightly different decorator factory-function which implements adding all the tags at once, so it can generate code that avoids the addtional function calls incurred by using separate decorators for each tag.

This requires more code in the decorator itself, but this only runs when it’s being applied to function definitions, not later when they themselves are called. This also applies when creating more readable names by using lambda functions as previously illustrated. Sample:

def multi_html_deco(*tags):
    start_tags, end_tags = [], []
    for tag in tags:
        start_tags.append('<%s>' % tag)
        end_tags.append('</%s>' % tag)
    start_tags = ''.join(start_tags)
    end_tags = ''.join(reversed(end_tags))

    def decorator(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            return start_tags + fn(*args, **kwargs) + end_tags
        return wrapped
    return decorator

makebolditalic = multi_html_deco('b', 'i')

@makebolditalic
def greet(whom=''):
    return 'Hello' + (' ' + whom) if whom else ''

print(greet('world'))  # -> <b><i>Hello world</i></b>

回答 7

做同样事情的另一种方式:

class bol(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<b>{}</b>".format(self.f())

class ita(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<i>{}</i>".format(self.f())

@bol
@ita
def sayhi():
  return 'hi'

或者,更灵活:

class sty(object):
  def __init__(self, tag):
    self.tag = tag
  def __call__(self, f):
    def newf():
      return "<{tag}>{res}</{tag}>".format(res=f(), tag=self.tag)
    return newf

@sty('b')
@sty('i')
def sayhi():
  return 'hi'

Another way of doing the same thing:

class bol(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<b>{}</b>".format(self.f())

class ita(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<i>{}</i>".format(self.f())

@bol
@ita
def sayhi():
  return 'hi'

Or, more flexibly:

class sty(object):
  def __init__(self, tag):
    self.tag = tag
  def __call__(self, f):
    def newf():
      return "<{tag}>{res}</{tag}>".format(res=f(), tag=self.tag)
    return newf

@sty('b')
@sty('i')
def sayhi():
  return 'hi'

回答 8

如何在Python中制作两个装饰器,以完成以下工作?

调用时需要以下函数:

@makebold
@makeitalic
def say():
    return "Hello"

回来:

<b><i>Hello</i></b>

简单的解决方案

为了最简单地执行此操作,请使装饰器返回关闭函数(闭包)的lambda(匿名函数)并调用它:

def makeitalic(fn):
    return lambda: '<i>' + fn() + '</i>'

def makebold(fn):
    return lambda: '<b>' + fn() + '</b>'

现在根据需要使用它们:

@makebold
@makeitalic
def say():
    return 'Hello'

现在:

>>> say()
'<b><i>Hello</i></b>'

简单解决方案的问题

但是我们似乎几乎失去了原来的功能。

>>> say
<function <lambda> at 0x4ACFA070>

为了找到它,我们需要深入研究每个lambda的闭包,其中一个埋藏在另一个中:

>>> say.__closure__[0].cell_contents
<function <lambda> at 0x4ACFA030>
>>> say.__closure__[0].cell_contents.__closure__[0].cell_contents
<function say at 0x4ACFA730>

因此,如果我们在此函数上放上文档,或者希望能够修饰带有多个参数的函数,或者我们只想知道在调试会话中要查看的函数,则需要对我们做更多的事情包装纸。

功能齐全的解决方案-克服了大多数此类问题

我们在标准库中提供wrapsfunctools模块中的装饰器!

from functools import wraps

def makeitalic(fn):
    # must assign/update attributes from wrapped function to wrapper
    # __module__, __name__, __doc__, and __dict__ by default
    @wraps(fn) # explicitly give function whose attributes it is applying
    def wrapped(*args, **kwargs):
        return '<i>' + fn(*args, **kwargs) + '</i>'
    return wrapped

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return '<b>' + fn(*args, **kwargs) + '</b>'
    return wrapped

不幸的是,仍然有一些样板,但这大约很简单。

在Python 3中,默认情况下也会获取__qualname____annotations__分配。

所以现在:

@makebold
@makeitalic
def say():
    """This function returns a bolded, italicized 'hello'"""
    return 'Hello'

现在:

>>> say
<function say at 0x14BB8F70>
>>> help(say)
Help on function say in module __main__:

say(*args, **kwargs)
    This function returns a bolded, italicized 'hello'

结论

因此,我们看到,wraps使包装函数几乎可以执行所有操作,只是确切告诉我们该函数将其用作参数。

还有其他模块可以尝试解决该问题,但是标准库中尚未提供该解决方案。

How can I make two decorators in Python that would do the following?

You want the following function, when called:

@makebold
@makeitalic
def say():
    return "Hello"

To return:

<b><i>Hello</i></b>

Simple solution

To most simply do this, make decorators that return lambdas (anonymous functions) that close over the function (closures) and call it:

def makeitalic(fn):
    return lambda: '<i>' + fn() + '</i>'

def makebold(fn):
    return lambda: '<b>' + fn() + '</b>'

Now use them as desired:

@makebold
@makeitalic
def say():
    return 'Hello'

and now:

>>> say()
'<b><i>Hello</i></b>'

Problems with the simple solution

But we seem to have nearly lost the original function.

>>> say
<function <lambda> at 0x4ACFA070>

To find it, we’d need to dig into the closure of each lambda, one of which is buried in the other:

>>> say.__closure__[0].cell_contents
<function <lambda> at 0x4ACFA030>
>>> say.__closure__[0].cell_contents.__closure__[0].cell_contents
<function say at 0x4ACFA730>

So if we put documentation on this function, or wanted to be able to decorate functions that take more than one argument, or we just wanted to know what function we were looking at in a debugging session, we need to do a bit more with our wrapper.

Full featured solution – overcoming most of these problems

We have the decorator wraps from the functools module in the standard library!

from functools import wraps

def makeitalic(fn):
    # must assign/update attributes from wrapped function to wrapper
    # __module__, __name__, __doc__, and __dict__ by default
    @wraps(fn) # explicitly give function whose attributes it is applying
    def wrapped(*args, **kwargs):
        return '<i>' + fn(*args, **kwargs) + '</i>'
    return wrapped

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return '<b>' + fn(*args, **kwargs) + '</b>'
    return wrapped

It is unfortunate that there’s still some boilerplate, but this is about as simple as we can make it.

In Python 3, you also get __qualname__ and __annotations__ assigned by default.

So now:

@makebold
@makeitalic
def say():
    """This function returns a bolded, italicized 'hello'"""
    return 'Hello'

And now:

>>> say
<function say at 0x14BB8F70>
>>> help(say)
Help on function say in module __main__:

say(*args, **kwargs)
    This function returns a bolded, italicized 'hello'

Conclusion

So we see that wraps makes the wrapping function do almost everything except tell us exactly what the function takes as arguments.

There are other modules that may attempt to tackle the problem, but the solution is not yet in the standard library.


回答 9

以简单的方式说明装饰器:

带有:

@decor1
@decor2
def func(*args, **kwargs):
    pass

什么时候:

func(*args, **kwargs)

您确实做到了:

decor1(decor2(func))(*args, **kwargs)

To explain the decorator in a simple way:

With:

@decor1
@decor2
def func(*args, **kwargs):
    pass

When do:

func(*args, **kwargs)

You really do:

decor1(decor2(func))(*args, **kwargs)

回答 10

装饰器采用函数定义并创建一个新函数,该函数执行该函数并转换结果。

@deco
def do():
    ...

等效于:

do = deco(do)

例:

def deco(func):
    def inner(letter):
        return func(letter).upper()  #upper
    return inner

这个

@deco
def do(number):
    return chr(number)  # number to letter

相当于这个

def do2(number):
    return chr(number)

do2 = deco(do2)

65 <=>’a’

print(do(65))
print(do2(65))
>>> B
>>> B

要了解装饰器,必须注意,装饰器创建了一个新的函数do,该函数在内部执行函数并转换结果。

A decorator takes the function definition and creates a new function that executes this function and transforms the result.

@deco
def do():
    ...

is equivalent to:

do = deco(do)

Example:

def deco(func):
    def inner(letter):
        return func(letter).upper()  #upper
    return inner

This

@deco
def do(number):
    return chr(number)  # number to letter

is equivalent to this

def do2(number):
    return chr(number)

do2 = deco(do2)

65 <=> ‘a’

print(do(65))
print(do2(65))
>>> B
>>> B

To understand the decorator, it is important to notice, that decorator created a new function do which is inner that executes function and transforms the result.


回答 11

#decorator.py
def makeHtmlTag(tag, *args, **kwds):
    def real_decorator(fn):
        css_class = " class='{0}'".format(kwds["css_class"]) \
                                 if "css_class" in kwds else ""
        def wrapped(*args, **kwds):
            return "<"+tag+css_class+">" + fn(*args, **kwds) + "</"+tag+">"
        return wrapped
    # return decorator dont call it
    return real_decorator

@makeHtmlTag(tag="b", css_class="bold_css")
@makeHtmlTag(tag="i", css_class="italic_css")
def hello():
    return "hello world"

print hello()

您也可以在Class中编写装饰器

#class.py
class makeHtmlTagClass(object):
    def __init__(self, tag, css_class=""):
        self._tag = tag
        self._css_class = " class='{0}'".format(css_class) \
                                       if css_class != "" else ""

    def __call__(self, fn):
        def wrapped(*args, **kwargs):
            return "<" + self._tag + self._css_class+">"  \
                       + fn(*args, **kwargs) + "</" + self._tag + ">"
        return wrapped

@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
    return "Hello, {}".format(name)

print hello("Your name")
#decorator.py
def makeHtmlTag(tag, *args, **kwds):
    def real_decorator(fn):
        css_class = " class='{0}'".format(kwds["css_class"]) \
                                 if "css_class" in kwds else ""
        def wrapped(*args, **kwds):
            return "<"+tag+css_class+">" + fn(*args, **kwds) + "</"+tag+">"
        return wrapped
    # return decorator dont call it
    return real_decorator

@makeHtmlTag(tag="b", css_class="bold_css")
@makeHtmlTag(tag="i", css_class="italic_css")
def hello():
    return "hello world"

print hello()

You can also write decorator in Class

#class.py
class makeHtmlTagClass(object):
    def __init__(self, tag, css_class=""):
        self._tag = tag
        self._css_class = " class='{0}'".format(css_class) \
                                       if css_class != "" else ""

    def __call__(self, fn):
        def wrapped(*args, **kwargs):
            return "<" + self._tag + self._css_class+">"  \
                       + fn(*args, **kwargs) + "</" + self._tag + ">"
        return wrapped

@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
    return "Hello, {}".format(name)

print hello("Your name")

回答 12

这个答案早就得到了回答,但是我想我应该分享我的Decorator类,它使编写新的装饰器变得轻松而紧凑。

from abc import ABCMeta, abstractclassmethod

class Decorator(metaclass=ABCMeta):
    """ Acts as a base class for all decorators """

    def __init__(self):
        self.method = None

    def __call__(self, method):
        self.method = method
        return self.call

    @abstractclassmethod
    def call(self, *args, **kwargs):
        return self.method(*args, **kwargs)

我认为,这可以使修饰器的行为非常清晰,但是也可以很简洁地定义新的修饰器。对于上面列出的示例,您可以将其解决为:

class MakeBold(Decorator):
    def call():
        return "<b>" + self.method() + "</b>"

class MakeItalic(Decorator):
    def call():
        return "<i>" + self.method() + "</i>"

@MakeBold()
@MakeItalic()
def say():
   return "Hello"

您还可以使用它来执行更复杂的任务,例如装饰器,该装饰器自动使函数递归地应用于迭代器中的所有参数:

class ApplyRecursive(Decorator):
    def __init__(self, *types):
        super().__init__()
        if not len(types):
            types = (dict, list, tuple, set)
        self._types = types

    def call(self, arg):
        if dict in self._types and isinstance(arg, dict):
            return {key: self.call(value) for key, value in arg.items()}

        if set in self._types and isinstance(arg, set):
            return set(self.call(value) for value in arg)

        if tuple in self._types and isinstance(arg, tuple):
            return tuple(self.call(value) for value in arg)

        if list in self._types and isinstance(arg, list):
            return list(self.call(value) for value in arg)

        return self.method(arg)


@ApplyRecursive(tuple, set, dict)
def double(arg):
    return 2*arg

print(double(1))
print(double({'a': 1, 'b': 2}))
print(double({1, 2, 3}))
print(double((1, 2, 3, 4)))
print(double([1, 2, 3, 4, 5]))

哪些打印:

2
{'a': 2, 'b': 4}
{2, 4, 6}
(2, 4, 6, 8)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

请注意,此示例未list在装饰器的实例中包括类型,因此在最终的print语句中,方法将应用于列表本身,而不是列表的元素。

This answer has long been answered, but I thought I would share my Decorator class which makes writing new decorators easy and compact.

from abc import ABCMeta, abstractclassmethod

class Decorator(metaclass=ABCMeta):
    """ Acts as a base class for all decorators """

    def __init__(self):
        self.method = None

    def __call__(self, method):
        self.method = method
        return self.call

    @abstractclassmethod
    def call(self, *args, **kwargs):
        return self.method(*args, **kwargs)

For one I think this makes the behavior of decorators very clear, but it also makes it easy to define new decorators very concisely. For the example listed above, you could then solve it as:

class MakeBold(Decorator):
    def call():
        return "<b>" + self.method() + "</b>"

class MakeItalic(Decorator):
    def call():
        return "<i>" + self.method() + "</i>"

@MakeBold()
@MakeItalic()
def say():
   return "Hello"

You could also use it to do more complex tasks, like for instance a decorator which automatically makes the function get applied recursively to all arguments in an iterator:

class ApplyRecursive(Decorator):
    def __init__(self, *types):
        super().__init__()
        if not len(types):
            types = (dict, list, tuple, set)
        self._types = types

    def call(self, arg):
        if dict in self._types and isinstance(arg, dict):
            return {key: self.call(value) for key, value in arg.items()}

        if set in self._types and isinstance(arg, set):
            return set(self.call(value) for value in arg)

        if tuple in self._types and isinstance(arg, tuple):
            return tuple(self.call(value) for value in arg)

        if list in self._types and isinstance(arg, list):
            return list(self.call(value) for value in arg)

        return self.method(arg)


@ApplyRecursive(tuple, set, dict)
def double(arg):
    return 2*arg

print(double(1))
print(double({'a': 1, 'b': 2}))
print(double({1, 2, 3}))
print(double((1, 2, 3, 4)))
print(double([1, 2, 3, 4, 5]))

Which prints:

2
{'a': 2, 'b': 4}
{2, 4, 6}
(2, 4, 6, 8)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

Notice that this example didn’t include the list type in the instantiation of the decorator, so in the final print statement the method gets applied to the list itself, not the elements of the list.


回答 13

这是链接装饰器的简单示例。注意最后一行-它显示了幕后的内容。

############################################################
#
#    decorators
#
############################################################

def bold(fn):
    def decorate():
        # surround with bold tags before calling original function
        return "<b>" + fn() + "</b>"
    return decorate


def uk(fn):
    def decorate():
        # swap month and day
        fields = fn().split('/')
        date = fields[1] + "/" + fields[0] + "/" + fields[2]
        return date
    return decorate

import datetime
def getDate():
    now = datetime.datetime.now()
    return "%d/%d/%d" % (now.day, now.month, now.year)

@bold
def getBoldDate(): 
    return getDate()

@uk
def getUkDate():
    return getDate()

@bold
@uk
def getBoldUkDate():
    return getDate()


print getDate()
print getBoldDate()
print getUkDate()
print getBoldUkDate()
# what is happening under the covers
print bold(uk(getDate))()

输出如下:

17/6/2013
<b>17/6/2013</b>
6/17/2013
<b>6/17/2013</b>
<b>6/17/2013</b>

Here is a simple example of chaining decorators. Note the last line – it shows what is going on under the covers.

############################################################
#
#    decorators
#
############################################################

def bold(fn):
    def decorate():
        # surround with bold tags before calling original function
        return "<b>" + fn() + "</b>"
    return decorate


def uk(fn):
    def decorate():
        # swap month and day
        fields = fn().split('/')
        date = fields[1] + "/" + fields[0] + "/" + fields[2]
        return date
    return decorate

import datetime
def getDate():
    now = datetime.datetime.now()
    return "%d/%d/%d" % (now.day, now.month, now.year)

@bold
def getBoldDate(): 
    return getDate()

@uk
def getUkDate():
    return getDate()

@bold
@uk
def getBoldUkDate():
    return getDate()


print getDate()
print getBoldDate()
print getUkDate()
print getBoldUkDate()
# what is happening under the covers
print bold(uk(getDate))()

The output looks like:

17/6/2013
<b>17/6/2013</b>
6/17/2013
<b>6/17/2013</b>
<b>6/17/2013</b>

回答 14

说到计数器示例-如上所述,计数器将在使用装饰器的所有函数之间共享:

def counter(func):
    def wrapped(*args, **kws):
        print 'Called #%i' % wrapped.count
        wrapped.count += 1
        return func(*args, **kws)
    wrapped.count = 0
    return wrapped

这样,您的装饰器可以重用于不同的功能(或多次用于装饰相同的功能:)func_counter1 = counter(func); func_counter2 = counter(func),并且counter变量将对每个函数保持私有。

Speaking of the counter example – as given above, the counter will be shared between all functions that use the decorator:

def counter(func):
    def wrapped(*args, **kws):
        print 'Called #%i' % wrapped.count
        wrapped.count += 1
        return func(*args, **kws)
    wrapped.count = 0
    return wrapped

That way, your decorator can be reused for different functions (or used to decorate the same function multiple times: func_counter1 = counter(func); func_counter2 = counter(func)), and the counter variable will remain private to each.


回答 15

用不同数量的参数装饰函数:

def frame_tests(fn):
    def wrapper(*args):
        print "\nStart: %s" %(fn.__name__)
        fn(*args)
        print "End: %s\n" %(fn.__name__)
    return wrapper

@frame_tests
def test_fn1():
    print "This is only a test!"

@frame_tests
def test_fn2(s1):
    print "This is only a test! %s" %(s1)

@frame_tests
def test_fn3(s1, s2):
    print "This is only a test! %s %s" %(s1, s2)

if __name__ == "__main__":
    test_fn1()
    test_fn2('OK!')
    test_fn3('OK!', 'Just a test!')

结果:

Start: test_fn1  
This is only a test!  
End: test_fn1  


Start: test_fn2  
This is only a test! OK!  
End: test_fn2  


Start: test_fn3  
This is only a test! OK! Just a test!  
End: test_fn3  

Decorate functions with different number of arguments:

def frame_tests(fn):
    def wrapper(*args):
        print "\nStart: %s" %(fn.__name__)
        fn(*args)
        print "End: %s\n" %(fn.__name__)
    return wrapper

@frame_tests
def test_fn1():
    print "This is only a test!"

@frame_tests
def test_fn2(s1):
    print "This is only a test! %s" %(s1)

@frame_tests
def test_fn3(s1, s2):
    print "This is only a test! %s %s" %(s1, s2)

if __name__ == "__main__":
    test_fn1()
    test_fn2('OK!')
    test_fn3('OK!', 'Just a test!')

Result:

Start: test_fn1  
This is only a test!  
End: test_fn1  


Start: test_fn2  
This is only a test! OK!  
End: test_fn2  


Start: test_fn3  
This is only a test! OK! Just a test!  
End: test_fn3  

回答 16

保罗·贝尔甘蒂诺(Paolo Bergantino)的答案具有仅使用stdlib的巨大优势,并且适用于此简单示例,其中既没有装饰器参数,也没有装饰的函数参数。

但是,如果要处理更一般的情况,它有3个主要限制:

  • 正如已经在几个答案中指出的那样,您无法轻松地修改代码以添加可选的decorator参数。例如,创建makestyle(style='bold')装饰器并非易事。
  • 此外,使用创建的包装器@functools.wraps 不会保留签名,因此,如果提供了错误的参数,它们将开始执行,并且可能会引发与通常情况不同的错误TypeError
  • 最后,它是与创建包装相当困难@functools.wraps,以访问基于其名称的参数。实际上,该参数可以出现在*args,中**kwargs或完全不出现(如果它是可选的)。

我写decopatch来解决第一个问题,并写makefun.wraps来解决其他两个问题。请注意,makefun该方法与著名的decoratorlib 具有相同的技巧。

这是使用参数创建装饰器的方式,返回真正的保留签名的包装器:

from decopatch import function_decorator, DECORATED
from makefun import wraps

@function_decorator
def makestyle(st='b', fn=DECORATED):
    open_tag = "<%s>" % st
    close_tag = "</%s>" % st

    @wraps(fn)
    def wrapped(*args, **kwargs):
        return open_tag + fn(*args, **kwargs) + close_tag

    return wrapped

decopatch根据您的喜好,为您提供了另外两种隐藏或显示各种python概念的开发样式。最紧凑的样式如下:

from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS

@function_decorator
def makestyle(st='b', fn=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
    open_tag = "<%s>" % st
    close_tag = "</%s>" % st
    return open_tag + fn(*f_args, **f_kwargs) + close_tag

在这两种情况下,您都可以检查装饰器是否按预期工作:

@makestyle
@makestyle('i')
def hello(who):
    return "hello %s" % who

assert hello('world') == '<b><i>hello world</i></b>'    

有关详细信息,请参阅文档

Paolo Bergantino’s answer has the great advantage of only using the stdlib, and works for this simple example where there are no decorator arguments nor decorated function arguments.

However it has 3 major limitations if you want to tackle more general cases:

  • as already noted in several answers, you can not easily modify the code to add optional decorator arguments. For example creating a makestyle(style='bold') decorator is non-trivial.
  • besides, wrappers created with @functools.wraps do not preserve the signature, so if bad arguments are provided they will start executing, and might raise a different kind of error than the usual TypeError.
  • finally, it is quite difficult in wrappers created with @functools.wraps to access an argument based on its name. Indeed the argument can appear in *args, in **kwargs, or may not appear at all (if it is optional).

I wrote decopatch to solve the first issue, and wrote makefun.wraps to solve the other two. Note that makefun leverages the same trick than the famous decorator lib.

This is how you would create a decorator with arguments, returning truly signature-preserving wrappers:

from decopatch import function_decorator, DECORATED
from makefun import wraps

@function_decorator
def makestyle(st='b', fn=DECORATED):
    open_tag = "<%s>" % st
    close_tag = "</%s>" % st

    @wraps(fn)
    def wrapped(*args, **kwargs):
        return open_tag + fn(*args, **kwargs) + close_tag

    return wrapped

decopatch provides you with two other development styles that hide or show the various python concepts, depending on your preferences. The most compact style is the following:

from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS

@function_decorator
def makestyle(st='b', fn=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
    open_tag = "<%s>" % st
    close_tag = "</%s>" % st
    return open_tag + fn(*f_args, **f_kwargs) + close_tag

In both cases you can check that the decorator works as expected:

@makestyle
@makestyle('i')
def hello(who):
    return "hello %s" % who

assert hello('world') == '<b><i>hello world</i></b>'    

Please refer to the documentation for details.


__str__和__repr__之间的区别?

问题:__str__和__repr__之间的区别?

__str____repr__Python 和有什么不一样?

What is the difference between __str__ and __repr__ in Python?


回答 0

亚历克斯总结得很好,但令人惊讶的是,它太简洁了。

首先,让我重申亚历克斯(Alex)帖子中的要点:

  • 默认的实现是无用的(很难想到不会的,但是是的)
  • __repr__ 目标是明确
  • __str__ 目标是可读
  • 容器__str__使用的包含对象__repr__

默认实现是没有用的

这主要是令人惊讶的,因为Python的默认设置往往非常有用。但是,在这种情况下,具有默认值的__repr__行为如下:

return "%s(%r)" % (self.__class__, self.__dict__)

太危险了(例如,如果对象之间互相引用,则很容易陷入无限递归)。因此,Python应对了。请注意,有一个默认值为true:如果__repr__已定义,但未定义,__str__则该对象的行为就好像__str__=__repr__

简单来说,这意味着:您实现的几乎每个对象都应具有__repr__可用于理解该对象的功能。实施__str__是可选的:如果您需要“漂亮的打印”功能(例如,由报告生成器使用),请执行此操作。

的目标__repr__是明确的

我马上说出来-我不相信调试器。我真的不知道如何使用任何调试器,也从未认真使用过。此外,我相信调试器的最大缺陷是它们的基本特性–我调试的大多数故障是很久以前发生的,它位于一个遥远的星系中。这意味着我确实以宗教的热情相信伐木。日志记录是任何体面的“一劳永逸”服务器系统的命脉。使用Python可以轻松记录日志:也许有一些特定于项目的包装器,您只需要一个

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

但是,您必须做最后一步-确保实现的每个对象都有一个有用的代表,这样的代码才能正常工作。这就是为什么出现“评估”问题的原因:如果您有足够的信息eval(repr(c))==c,这意味着您知道所有要了解的信息c。如果那很容易(至少以一种模糊的方式),那就去做。如果没有,请确保您有足够的信息c。我通常使用类似eval的格式:"MyClass(this=%r,that=%r)" % (self.this,self.that)。这并不意味着您可以实际构造MyClass,也不意味着它们是正确的构造方法参数—但这是表达“这是您需要了解的有关该实例的一切”的有用形式。

注意:我在%r上面使用过,不是%s。您总是想在实现中使用repr()[或%r等效地格式化字符] __repr__,否则您就无法实现repr的目标。你要能够区分MyClass(3)MyClass("3")

的目标__str__是可读

具体来说,它并非旨在明确-请注意str(3)==str("3")。同样,如果实现IP抽象,则让其str看起来像192.168.1.1很好。在实现日期/时间抽象时,str可以是“ 2010/4/12 15:35:22”,等等。目标是以用户(而不是程序员)想要阅读的方式表示它。砍掉无用的数字,冒充其他类别-只要它支持可读性,它就是一种进步。

容器__str__使用的包含对象__repr__

这似乎令人惊讶,不是吗?它有点,但是如果使用它们,它们的可读性如何__str__

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

不是特别的。具体而言,容器中的字符串会发现太容易打乱其字符串表示形式。记住,面对歧义,Python抵制了猜测的诱惑。如果您在打印列表时需要上述行为,则只需

print "[" + ", ".join(l) + "]"

(您可能还可以弄清楚如何处理字典。

摘要

实现__repr__你实现任何类。这应该是第二天性。__str__如果您认为有一个字符串版本会影响可读性会很有用,请实施。

Alex summarized well but, surprisingly, was too succinct.

First, let me reiterate the main points in Alex’s post:

  • The default implementation is useless (it’s hard to think of one which wouldn’t be, but yeah)
  • __repr__ goal is to be unambiguous
  • __str__ goal is to be readable
  • Container’s __str__ uses contained objects’ __repr__

Default implementation is useless

This is mostly a surprise because Python’s defaults tend to be fairly useful. However, in this case, having a default for __repr__ which would act like:

return "%s(%r)" % (self.__class__, self.__dict__)

would have been too dangerous (for example, too easy to get into infinite recursion if objects reference each other). So Python cops out. Note that there is one default which is true: if __repr__ is defined, and __str__ is not, the object will behave as though __str__=__repr__.

This means, in simple terms: almost every object you implement should have a functional __repr__ that’s usable for understanding the object. Implementing __str__ is optional: do that if you need a “pretty print” functionality (for example, used by a report generator).

The goal of __repr__ is to be unambiguous

Let me come right out and say it — I do not believe in debuggers. I don’t really know how to use any debugger, and have never used one seriously. Furthermore, I believe that the big fault in debuggers is their basic nature — most failures I debug happened a long long time ago, in a galaxy far far away. This means that I do believe, with religious fervor, in logging. Logging is the lifeblood of any decent fire-and-forget server system. Python makes it easy to log: with maybe some project specific wrappers, all you need is a

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

But you have to do the last step — make sure every object you implement has a useful repr, so code like that can just work. This is why the “eval” thing comes up: if you have enough information so eval(repr(c))==c, that means you know everything there is to know about c. If that’s easy enough, at least in a fuzzy way, do it. If not, make sure you have enough information about c anyway. I usually use an eval-like format: "MyClass(this=%r,that=%r)" % (self.this,self.that). It does not mean that you can actually construct MyClass, or that those are the right constructor arguments — but it is a useful form to express “this is everything you need to know about this instance”.

Note: I used %r above, not %s. You always want to use repr() [or %r formatting character, equivalently] inside __repr__ implementation, or you’re defeating the goal of repr. You want to be able to differentiate MyClass(3) and MyClass("3").

The goal of __str__ is to be readable

Specifically, it is not intended to be unambiguous — notice that str(3)==str("3"). Likewise, if you implement an IP abstraction, having the str of it look like 192.168.1.1 is just fine. When implementing a date/time abstraction, the str can be “2010/4/12 15:35:22”, etc. The goal is to represent it in a way that a user, not a programmer, would want to read it. Chop off useless digits, pretend to be some other class — as long is it supports readability, it is an improvement.

Container’s __str__ uses contained objects’ __repr__

This seems surprising, doesn’t it? It is a little, but how readable would it be if it used their __str__?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

Not very. Specifically, the strings in a container would find it way too easy to disturb its string representation. In the face of ambiguity, remember, Python resists the temptation to guess. If you want the above behavior when you’re printing a list, just

print "[" + ", ".join(l) + "]"

(you can probably also figure out what to do about dictionaries.

Summary

Implement __repr__ for any class you implement. This should be second nature. Implement __str__ if you think it would be useful to have a string version which errs on the side of readability.


回答 1

我的经验法则: __repr__针对开发人员,__str__针对客户。

My rule of thumb: __repr__ is for developers, __str__ is for customers.


回答 2

除非您特别采取行动以确保其他情况,否则大多数类在以下两个方面均不会产生有用的结果:

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>> 

如您所见-没有区别,没有信息超出类和对象的id。如果仅覆盖两个…之一:

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>> 

如您所见,如果您覆盖__repr__,这也用于__str__,但反之则不。

要知道的其他关键要点:__str__在内置容器上__repr__,对包含的项目使用,而不是对__str__。而且,尽管在典型文档中找到了有关主题的字眼,但几乎没有人为使__repr__对象的字符串成为eval可用于构建相等对象的字符串而烦恼(这太难了,而且不知道相关模块的实际导入方式会使它实际上完全不可能)。

因此,我的建议是:着重于使__str__合理的人类可读性,并__repr__尽可能地做到模棱两可,即使这会干扰使__repr__的返回值可以接受为__eval__!的模糊不可实现的目标。

Unless you specifically act to ensure otherwise, most classes don’t have helpful results for either:

>>> class Sic(object): pass
... 
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>> 

As you see — no difference, and no info beyond the class and object’s id. If you only override one of the two…:

>>> class Sic(object): 
...   def __repr__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): return 'foo'
... 
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>> 

as you see, if you override __repr__, that’s ALSO used for __str__, but not vice versa.

Other crucial tidbits to know: __str__ on a built-on container uses the __repr__, NOT the __str__, for the items it contains. And, despite the words on the subject found in typical docs, hardly anybody bothers making the __repr__ of objects be a string that eval may use to build an equal object (it’s just too hard, AND not knowing how the relevant module was actually imported makes it actually flat out impossible).

So, my advice: focus on making __str__ reasonably human-readable, and __repr__ as unambiguous as you possibly can, even if that interferes with the fuzzy unattainable goal of making __repr__‘s returned value acceptable as input to __eval__!


回答 3

__repr__:python对象的表示形式,通常eval会将其转换回该对象

__str__:是您认为的文本形式的对象

例如

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

__repr__: representation of python object usually eval will convert it back to that object

__str__: is whatever you think is that object in text form

e.g.

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

回答 4

简而言之,的目标__repr__是明确且__str__可读。

这是一个很好的例子:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

阅读此文档以获取代表:

repr(object)

返回包含对象的可打印表示形式的字符串。这与转化产生的值相同(反引号)。能够以常规功能访问此操作有时很有用。对于许多类型,此函数会尝试返回一个字符串,该字符串将在传递给时产生一个具有相同值的对象eval(),否则表示形式是一个用尖括号括起来的字符串,其中包含对象类型的名称以及其他信息通常包括对象的名称和地址。类可以通过定义__repr__()方法来控制此函数为其实例返回的内容。

这是str的文档:

str(object='')

返回一个字符串,其中包含对象的可很好打印的表示形式。对于字符串,这将返回字符串本身。与的区别repr(object)在于,str(object)并非总是尝试返回可接受的字符串eval();它的目标是返回可打印的字符串。如果未提供任何参数,则返回空字符串''

In short, the goal of __repr__ is to be unambiguous and __str__ is to be readable.

Here is a good example:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

Read this documentation for repr:

repr(object)

Return a string containing a printable representation of an object. This is the same value yielded by conversions (reverse quotes). It is sometimes useful to be able to access this operation as an ordinary function. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a __repr__() method.

Here is the documentation for str:

str(object='')

Return a string containing a nicely printable representation of an object. For strings, this returns the string itself. The difference with repr(object) is that str(object) does not always attempt to return a string that is acceptable to eval(); its goal is to return a printable string. If no argument is given, returns the empty string, ''.


回答 5

__str____repr__Python 和有什么不一样?

__str__(读为“ dunder(双下划线)字符串”)和__repr__(读为“ dunder-repper”(对于“表示形式”))都是根据对象状态返回字符串的特殊方法。

__repr__如果__str__丢失,则提供备份行为。

因此,首先应该编写一个__repr__,使您可以从返回的字符串中重新实例化一个等效的对象,例如,使用eval或通过在Python Shell中的逐字符键入字符。

在以后的任何时候,只要__str__有人认为有必要,就可以为该实例的用户可读的字符串表示形式编写一个。

__str__

如果打印对象,或将其传递给formatstr.formatstr,则如果__str__定义了方法,则将调用该方法,否则__repr__将使用该方法。

__repr__

__repr__方法由内置函数调用,repr并且是在评估返回对象的表达式时在python shell上回显的方法。

由于它为提供了备份__str__,如果您只能写一个,请从__repr__

这是关于的内置帮助repr

repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

也就是说,对于大多数对象,如果键入所打印的内容repr,则应该能够创建等效对象。但这不是默认的实现。

默认实现 __repr__

默认对象__repr__是(C Python source)类似:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

这意味着默认情况下,您将打印对象所属的模块,类名以及其在内存中位置的十六进制表示形式,例如:

<__main__.Foo object at 0x7f80665abdd0>

这些信息不是很有用,但是无法得出如何准确地创建任何给定实例的规范表示的方法,它总比没有好,至少告诉我们如何在内存中唯一标识它。

怎么__repr__有用?

让我们看看使用Python Shell和datetime对象有多么有用。首先,我们需要导入datetime模块:

import datetime

如果datetime.now在外壳中调用,我们将看到重新创建等效的datetime对象所需的一切。这是由datetime创建的__repr__

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

如果我们打印日期时间对象,则会看到一种很好的人类可读(实际上是ISO)格式。这是通过datetime的实现的__str__

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

重新创建丢失的对象是一件简单的事情,因为我们没有通过复制和粘贴从__repr__输出中将其分配给变量,然后进行打印,然后将其与其他对象存储在相同的人类可读输出中:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

我该如何实施?

在开发过程中,如果可能的话,您将希望能够以相同状态再现对象。例如,这就是datetime对象的定义方式__repr__Python源)。由于复制此类对象所需的所有属性,它相当复杂:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

如果希望对象具有更易理解的表示形式,则可以执行__str__下一步。这是datetime对象(Python源)的实现方式__str__,它很容易实现,因为它已经具有以ISO格式显示它的功能:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

设置__repr__ = __str__

这是对这里提出设置的另一个答案的批评__repr__ = __str__

设置__repr__ = __str__很愚蠢- __repr__是一个后备功能__str____repr__在编写a之前应先写一个供开发人员在调试中使用的a __str__

你需要一个__str__只有当你需要的对象的文本表示。

结论

__repr__为您编写的对象进行定义,以便您和其他开发人员在开发过程中使用它时可以得到一个可重现的示例。定义__str__何时需要其人类可读的字符串表示形式。

What is the difference between __str__ and __repr__ in Python?

__str__ (read as “dunder (double-underscore) string”) and __repr__ (read as “dunder-repper” (for “representation”)) are both special methods that return strings based on the state of the object.

__repr__ provides backup behavior if __str__ is missing.

So one should first write a __repr__ that allows you to reinstantiate an equivalent object from the string it returns e.g. using eval or by typing it in character-for-character in a Python shell.

At any time later, one can write a __str__ for a user-readable string representation of the instance, when one believes it to be necessary.

__str__

If you print an object, or pass it to format, str.format, or str, then if a __str__ method is defined, that method will be called, otherwise, __repr__ will be used.

__repr__

The __repr__ method is called by the builtin function repr and is what is echoed on your python shell when it evaluates an expression that returns an object.

Since it provides a backup for __str__, if you can only write one, start with __repr__

Here’s the builtin help on repr:

repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

That is, for most objects, if you type in what is printed by repr, you should be able to create an equivalent object. But this is not the default implementation.

Default Implementation of __repr__

The default object __repr__ is (C Python source) something like:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

That means by default you’ll print the module the object is from, the class name, and the hexadecimal representation of its location in memory – for example:

<__main__.Foo object at 0x7f80665abdd0>

This information isn’t very useful, but there’s no way to derive how one might accurately create a canonical representation of any given instance, and it’s better than nothing, at least telling us how we might uniquely identify it in memory.

How can __repr__ be useful?

Let’s look at how useful it can be, using the Python shell and datetime objects. First we need to import the datetime module:

import datetime

If we call datetime.now in the shell, we’ll see everything we need to recreate an equivalent datetime object. This is created by the datetime __repr__:

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

If we print a datetime object, we see a nice human readable (in fact, ISO) format. This is implemented by datetime’s __str__:

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

It is a simple matter to recreate the object we lost because we didn’t assign it to a variable by copying and pasting from the __repr__ output, and then printing it, and we get it in the same human readable output as the other object:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

How do I implement them?

As you’re developing, you’ll want to be able to reproduce objects in the same state, if possible. This, for example, is how the datetime object defines __repr__ (Python source). It is fairly complex, because of all of the attributes needed to reproduce such an object:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

If you want your object to have a more human readable representation, you can implement __str__ next. Here’s how the datetime object (Python source) implements __str__, which it easily does because it already has a function to display it in ISO format:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

Set __repr__ = __str__?

This is a critique of another answer here that suggests setting __repr__ = __str__.

Setting __repr__ = __str__ is silly – __repr__ is a fallback for __str__ and a __repr__, written for developers usage in debugging, should be written before you write a __str__.

You need a __str__ only when you need a textual representation of the object.

Conclusion

Define __repr__ for objects you write so you and other developers have a reproducible example when using it as you develop. Define __str__ when you need a human readable string representation of it.


回答 6

在Hans Petter Langtangen 撰写的《用于计算科学Python脚本》一书的第358页中,明确指出:

  • 所述__repr__在所述物体的完整字符串表示目标;
  • __str__是返回一个字符串,不错的打印。

所以,我更喜欢将它们理解为

  • repr =复制
  • str =字符串(表示形式)

从用户的角度来看,尽管这是我在学习python时的一个误解。

在同一页面上还提供了一个很小但很好的示例,如下所示:

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

On page 358 of the book Python scripting for computational science by Hans Petter Langtangen, it clearly states that

  • The __repr__ aims at a complete string representation of the object;
  • The __str__ is to return a nice string for printing.

So, I prefer to understand them as

  • repr = reproduce
  • str = string (representation)

from the user’s point of view although this is a misunderstanding I made when learning python.

A small but good example is also given on the same page as follows:

Example

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

回答 7

除了给出的所有答案外,我想补充几点:

__repr__()当您在交互式python控制台上简单地写对象名称并按Enter时,将调用1)。

2)__str__()在带print语句的对象上使用时被调用。

3)如果__str__丢失,则打印并使用对象str()调用__repr__()进行任何功能。

4)__str__()容器,当被调用时将执行__repr__()其所包含元素的方法。

5)str()在内部调用__str__()可能会在没有基本情况的情况下递归,并且最大递归深度会出错。

6)__repr__()可以调用repr(),它将尝试自动避免无限递归,将表示的对象替换为...

Apart from all the answers given, I would like to add few points :-

1) __repr__() is invoked when you simply write object’s name on interactive python console and press enter.

2) __str__() is invoked when you use object with print statement.

3) In case, if __str__ is missing, then print and any function using str() invokes __repr__() of object.

4) __str__() of containers, when invoked will execute __repr__() method of its contained elements.

5) str() called within __str__() could potentially recurse without a base case, and error on maximum recursion depth.

6) __repr__() can call repr() which will attempt to avoid infinite recursion automatically, replacing an already represented object with ....


回答 8

老实说,eval(repr(obj))从未使用过。如果发现自己正在使用它,则应该停止操作,因为这样做eval很危险,而字符串是序列化对象(pickle替代使用)的效率很低的方法。

因此,我建议设置__repr__ = __str__。原因是str(list)调用repr元素(我认为这是Python 3未能解决的Python最大设计缺陷之一)。实际repr输出可能不会非常有用print [your, objects]

为了证明这一点,以我的经验,该repr函数最有用的用例是将一个字符串放入另一个字符串中(使用字符串格式)。这样,您不必担心转义引号或其他内容。但是请注意,这里没有eval发生任何事情。

In all honesty, eval(repr(obj)) is never used. If you find yourself using it, you should stop, because eval is dangerous, and strings are a very inefficient way to serialize your objects (use pickle instead).

Therefore, I would recommend setting __repr__ = __str__. The reason is that str(list) calls repr on the elements (I consider this to be one of the biggest design flaws of Python that was not addressed by Python 3). An actual repr will probably not be very helpful as the output of print [your, objects].

To qualify this, in my experience, the most useful use case of the repr function is to put a string inside another string (using string formatting). This way, you don’t have to worry about escaping quotes or anything. But note that there is no eval happening here.


回答 9

简而言之:

__str__用于显示对象的字符串表示形式,以方便他人阅读

__repr__用于显示的字符串表示对象。

假设我要创建一个Fraction类,其中分数的字符串表示形式为“(1/2)”,而对象(分数类)将表示为“分数(1,2)”

因此,我们可以创建一个简单的Fraction类:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

To put it simply:

__str__ is used in to show a string representation of your object to be read easily by others.

__repr__ is used to show a string representation of the object.

Let’s say I want to create a Fraction class where the string representation of a fraction is ‘(1/2)’ and the object (Fraction class) is to be represented as ‘Fraction (1,2)’

So we can create a simple Fraction class:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

回答 10

来自effbot 的(非官方)Python参考Wiki(归档副本)

__str__计算‘正规’的对象的字符串表示这不同于。__repr__在它不必须是一个有效的Python表达式:一个更方便或简洁表示可以被代替使用。

From an (An Unofficial) Python Reference Wiki (archive copy) by effbot:

__str__computes the “informal” string representation of an object. This differs from __repr__ in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead.


回答 11

str -从给定的对象创建一个新的字符串对象。

repr -返回对象的规范字符串表示形式。

区别:

str():

  • 使对象可读
  • 为最终用户生成输出

repr():

  • 需要复制对象的代码
  • 为开发人员生成输出

str – Creates a new string object from the given object.

repr – Returns the canonical string representation of the object.

The differences:

str():

  • makes object readable
  • generates output for end-user

repr():

  • needs code that reproduces object
  • generates output for developer

回答 12

其他答案中缺少的一个方面。的确,该模式通常是:

  • 目标__str__:人类可读
  • 目标__repr__:明确,可能通过机器读取eval

不幸的是,这种区别是有缺陷的,因为Python REPL和IPython都__repr__用于在REPL控制台中打印对象(请参阅PythonIPython的相关问题)。因此,以交互式控制台工作为目标的项目(例如Numpy或Pandas)已经开始忽略上述规则,__repr__而是提供了易于理解的实现方式。

One aspect that is missing in other answers. It’s true that in general the pattern is:

  • Goal of __str__: human-readable
  • Goal of __repr__: unambiguous, possibly machine-readable via eval

Unfortunately, this differentiation is flawed, because the Python REPL and also IPython use __repr__ for printing objects in a REPL console (see related questions for Python and IPython). Thus, projects which are targeted for interactive console work (e.g., Numpy or Pandas) have started to ignore above rules and provide a human-readable __repr__ implementation instead.


回答 13

摘自Fluent Python一书:

Python对象的基本要求是提供其自身的可用字符串表示形式,一种用于调试和记录,另一种用于呈现给最终用户。这就是为什么
特殊方法__repr____str__存在于数据模型中的原因。

From the book Fluent Python:

A basic requirement for a Python object is to provide usable string representations of itself, one used for debugging and logging, another for presentation to end users. That is why the
special methods __repr__ and __str__ exist in the data model.


回答 14

出色的答案已经涵盖了__str__和之间的区别__repr__,对我而言,这归结为前者甚至对于最终用户而言都是可读的,而后者对开发人员则尽可能有用。鉴于此,我发现的默认实现__repr__常常无法实现此目标,因为它忽略了对开发人员有用的信息。

出于这个原因,如果我有一个简单的方法__str__,我通常会尝试通过以下方法使两个方面都达到最佳:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

Excellent answers already cover the difference between __str__ and __repr__, which for me boils down to the former being readable even by an end user, and the latter being as useful as possible to developers. Given that, I find that the default implementation of __repr__ often fails to achieve this goal because it omits information useful to developers.

For this reason, if I have a simple enough __str__, I generally just try to get the best of both worlds with something like:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

回答 15

要记住的重要一件事是容器的__str__使用包含对象__repr__

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python主张明确性胜于可读性__str__调用tuple调用包含的对象’ __repr__,即对象的“正式”表示形式。尽管正式表示比非正式表示更难读,但它对歧义没有任何歧义,并且更强大。

One important thing to keep in mind is that container’s __str__ uses contained objects’ __repr__.

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python favors unambiguity over readability, the __str__ call of a tuple calls the contained objects’ __repr__, the “formal” representation of an object. Although the formal representation is harder to read than an informal one, it is unambiguous and more robust against bugs.


回答 16

简而言之:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

In a nutshell:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

回答 17

>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

原始数据print()的结果decimal.Decimal(23) / decimal.Decimal("1.05")被打印时被调用;此输出为字符串形式,可以使用来实现__str__()。如果仅输入表达式,我们将得到一个decimal.Decimal输出-该输出采用可通过表示的形式__repr__()。所有Python对象都有两种输出形式。字符串形式被设计为易于阅读。表示形式旨在产生输出,如果将其提供给Python解释器,则该输出将(如果可能)再现所表示的对象。

>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

When print() is called on the result of decimal.Decimal(23) / decimal.Decimal("1.05") the raw number is printed; this output is in string form which can be achieved with __str__(). If we simply enter the expression we get a decimal.Decimal output — this output is in representational form which can be achieved with __repr__(). All Python objects have two output forms. String form is designed to be human-readable. The representational form is designed to produce output that if fed to a Python interpreter would (when possible) reproduce the represented object.


回答 18

__str__可以通过调用在对象上调用,str(obj)并且应返回人类可读的字符串。

__repr__可以通过调用在对象上调用,repr(obj)并且应该返回内部对象(对象字段/属性)

此示例可能会有所帮助:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

__str__ can be invoked on an object by calling str(obj) and should return a human readable string.

__repr__ can be invoked on an object by calling repr(obj) and should return internal object (object fields/attributes)

This example may help:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

回答 19

理解__str____repr__直观,永久地将它们区分开。

__str__返回给定对象的字符串伪装体,以使眼睛可读
__repr__

在一个例子中看到它

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

至于 __repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

我们可以__repr__方便地对结果进行算术运算。

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

如果将操作应用于 __str__

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

只返回错误。

另一个例子。

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

希望这可以帮助您建立具体的基础,以探索更多的答案。

Understand __str__ and __repr__ intuitively and permanently distinguish them at all.

__str__ return the string disguised body of a given object for readable of eyes
__repr__ return the real flesh body of a given object (return itself) for unambiguity to identify.

See it in an example

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

As to __repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

We can do arithmetic operation on __repr__ results conveniently.

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

if apply the operation on __str__

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Returns nothing but error.

Another example.

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

Hope this help you build concrete grounds to explore more answers.


回答 20

  1. __str__必须返回字符串对象,而__repr__可以返回任何python表达式。
  2. 如果__str__缺少实现,则将__repr__功能用作备用。如果__repr__缺少函数实现,则没有回退。
  3. 如果__repr__函数返回对象的字符串表示形式,则可以跳过__str__函数的实现。

资料来源:https : //www.journaldev.com/22460/python-str-repr-functions

  1. __str__ must return string object whereas __repr__ can return any python expression.
  2. If __str__ implementation is missing then __repr__ function is used as fallback. There is no fallback if __repr__ function implementation is missing.
  3. If __repr__ function is returning String representation of the object, we can skip implementation of __str__ function.

Source: https://www.journaldev.com/22460/python-str-repr-functions


回答 21

__repr__用于除printstr方法(__str__定义a时!)之外的所有地方

__repr__ is used everywhere, except by print and str methods (when a __str__is defined !)


检查字典中是否已存在给定键

问题:检查字典中是否已存在给定键

我想在更新密钥值之前测试字典中是否存在密钥。我写了以下代码:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

我认为这不是完成此任务的最佳方法。有没有更好的方法来测试字典中的键?

I wanted to test if a key exists in a dictionary before updating the value for the key. I wrote the following code:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

I think this is not the best way to accomplish this task. Is there a better way to test for a key in the dictionary?


回答 0

in是测试密钥是否存在的预期方法dict

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

如果您想使用默认值,可以随时使用dict.get()

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

如果您想始终确保任何键的默认值,则可以dict.setdefault()重复使用,也可以defaultdictcollections模块中使用,例如:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

但总的来说,in关键字是最好的方法。

in is the intended way to test for the existence of a key in a dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

If you wanted a default, you can always use dict.get():

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

and if you wanted to always ensure a default value for any key you can either use dict.setdefault() repeatedly or defaultdict from the collections module, like so:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

but in general, the in keyword is the best way to do it.


回答 1

您不必呼叫按键:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

这将更快,因为它使用字典的哈希而不是进行线性搜索(调用键可以做到)。

You don’t have to call keys:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

That will be much faster as it uses the dictionary’s hashing as opposed to doing a linear search, which calling keys would do.


回答 2

您可以使用in关键字测试字典中是否存在键:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

在更改字典之前,在字典中检查键是否存在的常见用途是对值进行默认初始化(例如,如果您的值是列表,并且您想确保可以在其后附加一个空列表)在插入键的第一个值时)。在这种情况下,您可能会发现collections.defaultdict()感兴趣的类型。

在较旧的代码中,您可能还会发现的某些用法has_key(),这是一种不赞成使用的方法,用于检查字典中键的存在(仅使用key_name in dict_name,而不是)。

You can test for the presence of a key in a dictionary, using the in keyword:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

A common use for checking the existence of a key in a dictionary before mutating it is to default-initialize the value (e.g. if your values are lists, for example, and you want to ensure that there is an empty list to which you can append when inserting the first value for a key). In cases such as those, you may find the collections.defaultdict() type to be of interest.

In older code, you may also find some uses of has_key(), a deprecated method for checking the existence of keys in dictionaries (just use key_name in dict_name, instead).


回答 3

您可以缩短此时间:

if 'key1' in dict:
    ...

但是,这充其量是对化妆品的改善。为什么您认为这不是最好的方法?

You can shorten this:

if 'key1' in dict:
    ...

However, this is at best a cosmetic improvement. Why do you believe this is not the best way?


回答 4

有关快速执行接受的答案的建议方法(10m循环)的其他信息:

  • 'key' in mydict 经过时间1.07秒
  • mydict.get('key') 经过时间1.84秒
  • mydefaultdict['key'] 经过时间1.07秒

因此,建议使用indefaultdict反对get

For additional info on speed execution of the accepted answer’s proposed methods (10m loops):

  • 'key' in mydict elapsed time 1.07 sec
  • mydict.get('key') elapsed time 1.84 sec
  • mydefaultdict['key'] elapsed time 1.07 sec

Therefore using in or defaultdict are recommended against get.


回答 5

我建议改用该setdefault方法。听起来它将满足您的所有要求。

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

I would recommend using the setdefault method instead. It sounds like it will do everything you want.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

回答 6

python中的Dictionary具有get(’key’,default)方法。因此,您可以在没有密钥的情况下设置默认值。

values = {...}
myValue = values.get('Key', None)

Dictionary in python has a get(‘key’, default) method. So you can just set a default value in case there is no key.

values = {...}
myValue = values.get('Key', None)

回答 7

如何使用EAFP(比请求更容易获得宽恕):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

查看其他SO帖子:

使用try VS如果Python

在Python中检查成员是否存在

What about using EAFP (easier to ask forgiveness than permission):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

See other SO posts:

Using try vs if in python or

Checking for member existence in Python


回答 8

使用三元运算符:

message = "blah" if 'key1' in dict else "booh"
print(message)

Using ternary operator:

message = "blah" if 'key1' in dict else "booh"
print(message)

回答 9

获得结果的方法是:

哪个更好取决于三个因素:

  1. 字典“通常没有钥匙”还是“通常没有钥匙”。
  2. 您是否打算使用if … else … elseif … else之类的条件?
  3. 字典有多大?

了解更多:http : //paltman.com/try-except-performance-in-python-a-simple-test/

使用try / block代替“ in”或“ if”:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

The ways in which you can get the results are:

Which is better is dependent on 3 things:

  1. Does the dictionary ‘normally has the key’ or ‘normally does not have the key’.
  2. Do you intend to use conditions like if…else…elseif…else?
  3. How big is dictionary?

Read More: http://paltman.com/try-except-performance-in-python-a-simple-test/

Use of try/block instead of ‘in’ or ‘if’:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

回答 10

仅限于Python 2 :(并且python 2.7 in已经支持)

您可以使用has_key()方法:

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

Python 2 only: (and python 2.7 supports in already)

you can use the has_key() method:

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

回答 11

只是克里斯的补充。B(最佳答案):

d = defaultdict(int)

也可以;原因是调用int()返回0defaultdict后台操作(在构造字典时),因此在文档中称为“工厂功能”。

Just an FYI adding to Chris. B (best answer):

d = defaultdict(int)

Works as well; the reason is that calling int() returns 0 which is what defaultdict does behind the scenes (when constructing a dictionary), hence the name “Factory Function” in the documentation.


回答 12

检查字典中是否已存在给定键

为了了解如何做到这一点,我们首先检查可以在字典上调用哪些方法。方法如下:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

检查密钥是否已存在的残酷方法可能是get()

d.get("key")

其他两种有趣的方法items()keys()听起来工作量太大。因此,让我们检查一下get()是否适合我们。我们有我们的字典d

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

打印显示我们没有的密钥将返回None

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

如果密钥存在或不存在,我们可能会用它来获取信息。但是,如果我们使用单个命令创建字典,请考虑以下问题key:None

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

get()如果某些值可能是,导致该方法不可靠None。这个故事的结局应该更快乐。如果我们使用in比较器:

print('key' in d) #True
print('key2' in d) #False

我们得到正确的结果。我们可以检查一下Python字节码:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

这表明in比较运算符不仅比更加可靠,而且甚至更快get()

Check if a given key already exists in a dictionary

To get the idea how to do that we first inspect what methods we can call on dictionary. Here are the methods:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

The brutal method to check if the key already exists may be the get() method:

d.get("key")

The other two interesting methods items() and keys() sounds like too much of work. So let’s examine if get() is the right method for us. We have our dict d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Printing shows the key we don’t have will return None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

We may use that to get the info if the key is present or no. But consider this if we create a dict with a single key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Leading that get() method is not reliable in case some values may be None. This story should have a happier ending. If we use the in comparator:

print('key' in d) #True
print('key2' in d) #False

We get the correct results. We may examine the Python byte code:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

This shows that in compare operator is not just more reliable but even faster than get().


回答 13

Python字典具有称为的方法__contains__。如果字典具有键,则此方法将返回True,否则返回False。

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

Python dictionary has the method called __contains__. This method will return True if the dictionary has the key else returns False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

回答 14

共享使用布尔运算符检查密钥是否存在的另一种方法。

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

这返回

>>> blah
>>> blah
>>> boo
>>> boo

说明

首先,你应该知道,在Python, ,0None或长度为零的对象评估为False。其他所有内容的计算结果均为True。布尔运算从左到右求值,并且返回的操作数不是True或False。

让我们来看一个例子:

>>> 'Some string' or 1/0 
'Some string'
>>>

由于'Some string'评估为True,因此or不会评估其余的,因此不会产生除以零的误差。

但是,如果我们切换顺序,1/0则会首先评估订单并引发异常:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

我们可以使用此模式检查密钥是否存在。

(k in d and 'blah')

与…相同

if k in d:
    'blah'
else:
    False

如果键存在,这已经返回了正确的结果,但是我们希望它在不存在时打印“ boo”。因此,我们将结果or'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

Sharing one more way of checking if a key exists using boolean operators.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

This returns

>>> blah
>>> blah
>>> boo
>>> boo

Explanation

First you should know that in Python, 0, None, or objects with zero length evaluate to False. Everything else evaluates to True. Boolean operations are evaluated left to right and return the operand not True or False.

Let’s see an example:

>>> 'Some string' or 1/0 
'Some string'
>>>

Since 'Some string' evaluates to True, the rest of the or is not evaluated and there is no division by zero error raised.

But if we switch the order 1/0 is evaluated first and raises an exception:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

We can use this for pattern for checking if a key exists.

(k in d and 'blah')

does the same as

if k in d:
    'blah'
else:
    False

This already returns the correct result if the key exists, but we want it to print ‘boo’ when it doesn’t. So, we take the result and or it with 'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

回答 15

您可以使用for循环遍历字典并获取要在字典中找到的键的名称,然后使用if条件检查其是否存在:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

You can use for loop to iterate over the dictionary and get the name of key you want to find in the dictionary, after that check if it exist or not using if condition:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

如何通过引用传递变量?

问题:如何通过引用传递变量?

Python文档似乎尚不清楚参数是通过引用还是通过值传递,并且以下代码产生不变的值“原始”

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

我可以做些什么来通过实际引用传递变量吗?

The Python documentation seems unclear about whether parameters are passed by reference or value, and the following code produces the unchanged value ‘Original’

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

Is there something I can do to pass the variable by actual reference?


回答 0

参数通过赋值传递。其背后的理由是双重的:

  1. 传入的参数实际上是对象的引用(但引用是按值传递的)
  2. 有些数据类型是可变的,但有些不是

所以:

  • 如果将可变对象传递给方法,则该方法将获得对该对象的引用,并且可以对其进行突变,但是如果您将该引用重新绑定到该方法中,则外部作用域对此一无所知完成后,外部参考仍将指向原始对象。

  • 如果将不可变对象传递给方法,则仍然无法重新绑定外部引用,甚至无法使对象发生突变。

为了更加清楚,让我们举一些例子。

列表-可变类型

让我们尝试修改传递给方法的列表:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

由于传入的参数是对的引用outer_list,而不是其副本,因此我们可以使用变异列表方法对其进行更改,并使更改反映在外部作用域中。

现在,让我们看看当尝试更改作为参数传入的引用时会发生什么:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

输出:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

由于the_list参数是通过值传递的,因此为其分配新列表不会影响方法外部的代码。该the_list是副本outer_list的参考,我们不得不the_list指向一个新的列表,但没有办法改变,其中outer_list尖。

字符串-不可变的类型

它是不可变的,因此我们无能为力,无法更改字符串的内容

现在,让我们尝试更改参考

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

输出:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

同样,由于the_string参数是通过值传递的,因此为其分配新的字符串不会影响方法外部的代码。该the_string是副本outer_string的参考,我们不得不the_string指向一个新的字符串,但没有办法改变,其中outer_string尖。

我希望这可以使事情变得简单。

编辑:注意到这并不能回答@David最初询问的问题,“我可以做些什么来通过实际引用传递变量吗?”。让我们继续努力。

我们如何解决这个问题?

如@Andrea的答案所示,您可以返回新值。这不会改变事物传递的方式,但是可以让您获取想要的信息:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

如果您确实想避免使用返回值,则可以创建一个类来保存您的值,并将其传递给函数或使用现有的类,例如列表:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

尽管这看起来有点麻烦。

Arguments are passed by assignment. The rationale behind this is twofold:

  1. the parameter passed in is actually a reference to an object (but the reference is passed by value)
  2. some data types are mutable, but others aren’t

So:

  • If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart’s delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you’re done, the outer reference will still point at the original object.

  • If you pass an immutable object to a method, you still can’t rebind the outer reference, and you can’t even mutate the object.

To make it even more clear, let’s have some examples.

List – a mutable type

Let’s try to modify the list that was passed to a method:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

Since the parameter passed in is a reference to outer_list, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.

Now let’s see what happens when we try to change the reference that was passed in as a parameter:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Output:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Since the the_list parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list was a copy of the outer_list reference, and we had the_list point to a new list, but there was no way to change where outer_list pointed.

String – an immutable type

It’s immutable, so there’s nothing we can do to change the contents of the string

Now, let’s try to change the reference

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Output:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Again, since the the_string parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string was a copy of the outer_string reference, and we had the_string point to a new string, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It’s been noted that this doesn’t answer the question that @David originally asked, “Is there something I can do to pass the variable by actual reference?”. Let’s work on that.

How do we get around this?

As @Andrea’s answer shows, you could return the new value. This doesn’t change the way things are passed in, but does let you get the information you want back out:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Although this seems a little cumbersome.


回答 1

问题来自对Python中的变量有误解。如果您习惯了大多数传统语言,那么您将对以下顺序有一个心理模型:

a = 1
a = 2

您认为这a是存储值的存储位置1,然后将其更新以存储值2。这不是Python中的工作方式。相反,a首先将其作为对具有值的对象的引用1,然后将其重新分配为对具有value的对象的引用2。即使a不再引用第一个对象,这两个对象也可能继续存在。实际上,它们可以被程序中的许多其他引用共享。

当您使用参数调用函数时,将创建一个新引用,该引用引用传入的对象。这与函数调用中使用的引用是分开的,因此无法更新该引用并将其引用为新对象。在您的示例中:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable是对字符串对象的引用'Original'。调用时,Change您将创建var对该对象的第二个引用。在函数内部,您将引用重新分配var给另一个字符串对象'Changed',但是引用self.variable是独立的,并且不会更改。

解决此问题的唯一方法是传递一个可变对象。因为两个引用都引用同一个对象,所以对对象的任何更改都会在两个地方反映出来。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

The problem comes from a misunderstanding of what variables are in Python. If you’re used to most traditional languages, you have a mental model of what happens in the following sequence:

a = 1
a = 2

You believe that a is a memory location that stores the value 1, then is updated to store the value 2. That’s not how things work in Python. Rather, a starts as a reference to an object with the value 1, then gets reassigned as a reference to an object with the value 2. Those two objects may continue to coexist even though a doesn’t refer to the first one anymore; in fact they may be shared by any number of other references within the program.

When you call a function with a parameter, a new reference is created that refers to the object passed in. This is separate from the reference that was used in the function call, so there’s no way to update that reference and make it refer to a new object. In your example:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable is a reference to the string object 'Original'. When you call Change you create a second reference var to the object. Inside the function you reassign the reference var to a different string object 'Changed', but the reference self.variable is separate and does not change.

The only way around this is to pass a mutable object. Because both references refer to the same object, any changes to the object are reflected in both places.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

回答 2

我发现其他答案相当冗长和复杂,因此我创建了这个简单的图来解释Python处理变量和参数的方式。 在此处输入图片说明

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters. enter image description here


回答 3

它既不是值传递也不是引用传递-它是对象调用。参见Fredrik Lundh的观点:

http://effbot.org/zone/call-by-object.htm

这是一个重要的报价:

“ …变量[名称] 不是对象;它们不能由其他变量表示或由对象引用。”

在您的示例中,Change调用该方法时-为该方法创建了一个命名空间;并var在该命名空间中成为字符串object的名称'Original'。然后,该对象在两个命名空间中都有一个名称。接下来,var = 'Changed'绑定var到新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,忘记了该命名空间,以及字符串'Changed'

It is neither pass-by-value or pass-by-reference – it is call-by-object. See this, by Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

Here is a significant quote:

“…variables [names] are not objects; they cannot be denoted by other variables or referred to by objects.”

In your example, when the Change method is called–a namespace is created for it; and var becomes a name, within that namespace, for the string object 'Original'. That object then has a name in two namespaces. Next, var = 'Changed' binds var to a new string object, and thus the method’s namespace forgets about 'Original'. Finally, that namespace is forgotten, and the string 'Changed' along with it.


回答 4

考虑一下通过分配而不是通过引用/按值传递的内容。这样,就很清楚,只要您了解正常分配过程中发生了什么,就会发生什么情况。

因此,当将列表传递给函数/方法时,该列表将分配给参数名称。追加到列表将导致列表被修改。在函数重新分配列表不会更改原始列表,因为:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

由于不可变类型无法修改,因此它们看起来像是通过值传递-将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但不会更改原始变量的值。

Think of stuff being passed by assignment instead of by reference/by value. That way, it is always clear, what is happening as long as you understand what happens during the normal assignment.

So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Since immutable types cannot be modified, they seem like being passed by value – passing an int into a function means assigning the int to the function’s parameter. You can only ever reassign that, but it won’t change the original variables value.


回答 5

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为按对象调用:http : //effbot.org/zone/call-by-object.htm

对象在堆上分配,指向它们的指针可以在任何地方传递。

  • 当您进行诸如的分配时x = 1000,将创建一个字典条目,该条目将当前命名空间中的字符串“ x”映射到指向包含一千的整数对象的指针。

  • 当您使用来更新“ x”时x = 2000,将创建一个新的整数对象,并且字典将更新为指向该新对象。旧的一千个对象保持不变(取决于其他是否引用了该对象,该对象是否可以存活)。

  • 当您进行诸如的新分配时y = x,将创建一个新的字典条目“ y”,该条目指向与“ x”条目相同的对象。

  • 诸如字符串和整数之类的对象是不可变的。这仅表示没有任何方法可以在创建对象后更改该对象。例如,一旦创建了整数对象1000,它就永远不会改变。数学是通过创建新的整数对象完成的。

  • 像列表这样的对象是可变的。这意味着可以通过指向该对象的任何内容来更改该对象的内容。例如,x = []; y = x; x.append(10); print y将打印[10]。空列表已创建。“ x”和“ y”都指向同一列表。该追加方法变异(更新)列表中的对象(如添加一条记录到数据库),其结果是可见的两个“X”和“Y”(就像一个数据库更新将每一个连接到该数据库中可见)。

希望能为您解决问题。

Effbot (aka Fredrik Lundh) has described Python’s variable passing style as call-by-object: http://effbot.org/zone/call-by-object.htm

Objects are allocated on the heap and pointers to them can be passed around anywhere.

  • When you make an assignment such as x = 1000, a dictionary entry is created that maps the string “x” in the current namespace to a pointer to the integer object containing one thousand.

  • When you update “x” with x = 2000, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).

  • When you do a new assignment such as y = x, a new dictionary entry “y” is created that points to the same object as the entry for “x”.

  • Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.

  • Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y will print [10]. The empty list was created. Both “x” and “y” point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both “x” and “y” (just as a database update would be visible to every connection to that database).

Hope that clarifies the issue for you.


回答 6

从技术上讲,Python始终使用按引用传递值。我将重复其他答案以支持我的发言。

Python始终使用按引用传递值。也不exceptions。任何变量分配都意味着复制参考值。没有exceptions。任何变量都是绑定到参考值的名称。总是。

您可以将参考值视为目标对象的地址。该地址在使用时会自动取消引用。这样,使用参考值,似乎您可以直接使用目标对象。但是,两者之间总是存在一个参考,距离目标还有一步之遥。

这是证明Python使用引用传递的示例:

传递参数的图解示例

如果参数通过值传递,则外部lst不能被修改。绿色是目标对象(黑色是内部存储的值,红色是对象类型),黄色是内部具有参考值的存储器-绘制为箭头。蓝色实心箭头是传递给函数的参考值(通过虚线蓝色箭头路径)。丑陋的深黄色是内部词典。(实际上也可以将其绘制为绿色椭圆形。颜色和形状仅表示它是内部的。)

您可以使用id()内置函数来了解参考值是什么(即目标对象的地址)。

在编译语言中,变量是能够捕获类型值的内存空间。在Python中,变量是绑定到引用变量的名称(在内部以字符串形式捕获),该变量将引用值保存到目标对象。变量的名称是内部字典中的键,该字典项的值部分将参考值存储到目标。

参考值隐藏在Python中。没有用于存储参考值的任何明确的用户类型。但是,您可以将list元素(或任何其他合适的容器类型的元素)用作引用变量,因为所有容器都确实将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器内,仅包含对元素的引用。

Technically, Python always uses pass by reference values. I am going to repeat my other answer to support my statement.

Python always uses pass-by-reference values. There isn’t any exception. Any variable assignment means copying the reference value. No exception. Any variable is the name bound to the reference value. Always.

You can think about a reference value as the address of the target object. The address is automatically dereferenced when used. This way, working with the reference value, it seems you work directly with the target object. But there always is a reference in between, one step more to jump to the target.

Here is the example that proves that Python uses passing by reference:

Illustrated example of passing the argument

If the argument was passed by value, the outer lst could not be modified. The green are the target objects (the black is the value stored inside, the red is the object type), the yellow is the memory with the reference value inside — drawn as the arrow. The blue solid arrow is the reference value that was passed to the function (via the dashed blue arrow path). The ugly dark yellow is the internal dictionary. (It actually could be drawn also as a green ellipse. The colour and the shape only says it is internal.)

You can use the id() built-in function to learn what the reference value is (that is, the address of the target object).

In compiled languages, a variable is a memory space that is able to capture the value of the type. In Python, a variable is a name (captured internally as a string) bound to the reference variable that holds the reference value to the target object. The name of the variable is the key in the internal dictionary, the value part of that dictionary item stores the reference value to the target.

Reference values are hidden in Python. There isn’t any explicit user type for storing the reference value. However, you can use a list element (or element in any other suitable container type) as the reference variable, because all containers do store the elements also as references to the target objects. In other words, elements are actually not contained inside the container — only the references to elements are.


回答 7

Python中没有变量

理解参数传递的关键是停止考虑“变量”。Python中有名称和对象,它们一起看起来像变量,但是始终区分这三个名称很有用。

  1. Python具有名称和对象。
  2. 分配将名称绑定到对象。
  3. 将参数传递给函数还会将名称(函数的参数名称)绑定到对象。

这就是全部。可变性与这个问题无关。

例:

a = 1

这会将名称绑定a到具有值1的整数类型的对象。

b = x

这会将名称绑定到b该名称x当前绑定到的同一对象。之后,该b名称x不再与该名称相关。

请参阅Python 3语言参考中的3.14.2节。

如何阅读问题中的例子

在问题所示的代码中,该语句self.Change(self.variable)将名称var(在function的范围内Change)绑定到保存该值的对象,'Original'而赋值var = 'Changed'(在function的主体中Change)再次将同一个名称分配给其他对象(发生这种情况)也可以容纳一个字符串,但完全可以是其他东西)。

如何通过参考

因此,如果您要更改的对象是可变对象,则没有问题,因为所有内容均有效地通过引用传递。

如果它是一个不可变的对象(例如布尔,数字,字符串),则将其包装在一个可变的对象中。
对此的快速解决方案是一个元素列表(而不是self.variable,pass [self.variable]和函数Modify var[0])。
更加Python化的方法是引入一个琐碎的,一属性类。该函数接收该类的实例并操纵该属性。

There are no variables in Python

The key to understanding parameter passing is to stop thinking about “variables”. There are names and objects in Python and together they appear like variables, but it is useful to always distinguish the three.

  1. Python has names and objects.
  2. Assignment binds a name to an object.
  3. Passing an argument into a function also binds a name (the parameter name of the function) to an object.

That is all there is to it. Mutability is irrelevant to this question.

Example:

a = 1

This binds the name a to an object of type integer that holds the value 1.

b = x

This binds the name b to the same object that the name x is currently bound to. Afterward, the name b has nothing to do with the name x anymore.

See sections 3.1 and 4.2 in the Python 3 language reference.

How to read the example in the question

In the code shown in the question, the statement self.Change(self.variable) binds the name var (in the scope of function Change) to the object that holds the value 'Original' and the assignment var = 'Changed' (in the body of function Change) assigns that same name again: to some other object (that happens to hold a string as well but could have been something else entirely).

How to pass by reference

So if the thing you want to change is a mutable object, there is no problem, as everything is effectively passed by reference.

If it is an immutable object (e.g. a bool, number, string), the way to go is to wrap it in a mutable object.
The quick-and-dirty solution for this is a one-element list (instead of self.variable, pass [self.variable] and in the function modify var[0]).
The more pythonic approach would be to introduce a trivial, one-attribute class. The function receives an instance of the class and manipulates the attribute.


回答 8

我通常使用的一个简单技巧就是将其包装在列表中:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(是的,我知道这可能很不方便,但是有时候这样做很简单。)

A simple trick I normally use is to just wrap it in a list:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Yeah I know this can be inconvenient, but sometimes it is simple enough to do this.)


回答 9

(编辑-布莱尔已更新了他的非常受欢迎的答案,因此现在是准确的)

我认为重要的是要注意,当前职位(布莱尔·康拉德(Blair Conrad))的票数最高,尽管其结果正确无误,但根据其定义,这是误导性的,也是不正确的。尽管有许多语言(例如C)允许用户通过引用传递或通过值传递,但Python并不是其中一种。

大卫·库纳波(David Cournapeau)的答案指出了真正的答案,并解释了为什么布莱尔·康拉德(Blair Conrad)的帖子中的行为似乎是正确的,而定义却不正确。

就Python按值传递的程度而言,所有语言都是按值传递的,因为必须发送一些数据(“值”或“引用”)。但是,这并不意味着Python就象C程序员会想到的那样按值传递。

如果您想要该行为,Blair Conrad的答案很好。但是,如果您想了解为什么Python既不按值传递也不按引用传递的细节,请阅读David Cournapeau的答案。

(edit – Blair has updated his enormously popular answer so that it is now accurate)

I think it is important to note that the current post with the most votes (by Blair Conrad), while being correct with respect to its result, is misleading and is borderline incorrect based on its definitions. While there are many languages (like C) that allow the user to either pass by reference or pass by value, Python is not one of them.

David Cournapeau’s answer points to the real answer and explains why the behavior in Blair Conrad’s post seems to be correct while the definitions are not.

To the extent that Python is pass by value, all languages are pass by value since some piece of data (be it a “value” or a “reference”) must be sent. However, that does not mean that Python is pass by value in the sense that a C programmer would think of it.

If you want the behavior, Blair Conrad’s answer is fine. But if you want to know the nuts and bolts of why Python is neither pass by value or pass by reference, read David Cournapeau’s answer.


回答 10

您在这里得到了一些非常好的答案。

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

You got some really good answers here.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

回答 11

在这种情况下,var方法中标题为该变量Change的引用将分配给self.variable,然后您立即将一个字符串分配给var。它不再指向self.variable。以下代码段显示了如果修改由var和指向的数据结构self.variable(在本例中为列表)将会发生的情况:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

我相信其他人可以进一步澄清这一点。

In this case the variable titled var in the method Change is assigned a reference to self.variable, and you immediately assign a string to var. It’s no longer pointing to self.variable. The following code snippet shows what would happen if you modify the data structure pointed to by var and self.variable, in this case a list:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

I’m sure someone else could clarify this further.


回答 12

Python的传递分配方案与C ++的引用参数选项不太相同,但实际上与C语言(和其他语言)的参数传递模型非常相似:

  • 不变的参数有效地“ 按值 ” 传递。诸如整数和字符串之类的对象是通过对象引用传递的,而不是通过复制传递的,但是由于您无论如何都不能就地更改不可变对象,因此效果很像制作副本。
  • 可变参数有效地“ 通过指针 ” 传递。诸如列表和字典之类的对象也通过对象引用传递,这类似于C将数组作为指针传递的方式—可变对象可以在函数中就地更改,就像C数组一样。

Python’s pass-by-assignment scheme isn’t quite the same as C++’s reference parameters option, but it turns out to be very similar to the argument-passing model of the C language (and others) in practice:

  • Immutable arguments are effectively passed “by value.” Objects such as integers and strings are passed by object reference instead of by copying, but because you can’t change immutable objects in place anyhow, the effect is much like making a copy.
  • Mutable arguments are effectively passed “by pointer.” Objects such as lists and dictionaries are also passed by object reference, which is similar to the way C passes arrays as pointers—mutable objects can be changed in place in the function, much like C arrays.

回答 13

如您所言,您需要有一个可变对象,但让我建议您检查一下全局变量,因为它们可以帮助您甚至解决此类问题!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

例:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

As you can state you need to have a mutable object, but let me suggest you to check over the global variables as they can help you or even solve this kind of issue!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

example:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

回答 14

答案中有很多见解,但我认为此处没有明确提及其他要点。引用python文档https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“在Python中,仅在函数内部引用的变量是隐式全局的。如果在函数体内的任何位置为变量分配了新值,则假定该变量是局部的。如果在函数内部分配了新值,该变量是隐式局部变量,您需要将其显式声明为“全局”变量,尽管起初有些令人惊讶,但片刻的考虑可以解释这一点:一方面,要求全局变量赋值可防止意外副作用。另一方面,如果所有全局引用都需要使用global,那么您将一直使用global,您必须将对内置函数或导入模块的组件的每个引用声明为全局。将破坏全球宣言对确定副作用的有用性。”

即使将可变对象传递给函数,这仍然适用。并且向我清楚地解释了在函数中分配给对象和对对象进行操作之间行为不同的原因。

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

给出:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

因此,对未声明为全局变量的全局变量的分配将创建一个新的本地对象,并断开与原始对象的链接。

A lot of insights in answers here, but i think an additional point is not clearly mentioned here explicitly. Quoting from python documentation https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.”

Even when passing a mutable object to a function this still applies. And to me clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

gives:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.


回答 15

这是pass by objectPython中使用的概念的简单解释(我希望如此)。
每当将对象传递给函数时,都会传递对象本身(Python中的对象实际上是您在其他编程语言中称为值的对象),而不是对该对象的引用。换句话说,当您调用时:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

正在传递实际对象[0,1](在其他编程语言中将其称为值)。因此,实际上该函数change_me将尝试执行以下操作:

[0, 1] = [1, 2, 3]

这显然不会改变传递给函数的对象。如果函数看起来像这样:

def change_me(list):
   list.append(2)

然后,该调用将导致:

[0, 1].append(2)

这显然会改变对象。这个答案很好地解释了。

Here is the simple (I hope) explanation of the concept pass by object used in Python.
Whenever you pass an object to the function, the object itself is passed (object in Python is actually what you’d call a value in other programming languages) not the reference to this object. In other words, when you call:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

The actual object – [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

[0, 1] = [1, 2, 3]

which obviously will not change the object passed to the function. If the function looked like this:

def change_me(list):
   list.append(2)

Then the call would result in:

[0, 1].append(2)

which obviously will change the object. This answer explains it well.


回答 16

除了对这些东西如何在Python中工作的所有出色解释之外,我没有看到关于该问题的简单建议。正如您似乎确实要创建对象和实例一样,处理实例变量并更改它们的pythonic方法如下:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

在实例方法中,通常引用self访问实例属性。通常__init__在实例方法中设置实例属性并对其进行读取或更改。这也是为什么您将selfals的第一个参数传递给def Change

另一个解决方案是创建一个像这样的静态方法:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

Aside from all the great explanations on how this stuff works in Python, I don’t see a simple suggestion for the problem. As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

In instance methods, you normally refer to self to access instance attributes. It is normal to set instance attributes in __init__ and read or change them in instance methods. That is also why you pass self als the first argument to def Change.

Another solution would be to create a static method like this:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

回答 17

即使该语言无法实现通过引用传递对象的小技巧,它也适用于Java,它是包含一项的列表。;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

这是一个丑陋的骇客,但确实有效。;-P

There is a little trick to pass an object by reference, even though the language doesn’t make it possible. It works in Java too, it’s the list with one item. ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

It’s an ugly hack, but it works. ;-P


回答 18

我使用以下方法快速将几个Fortran代码转换为Python。是的,它并没有像最初的问题那样被引用,而是在某些情况下是一种简单的解决方法。

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

I used the following method to quickly convert a couple of Fortran codes to Python. True, it’s not pass by reference as the original question was posed, but is a simple work around in some cases.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

回答 19

虽然按引用传递并不是最适合python的东西,并且不应该使用,但是有些变通办法实际上可以起作用,以获取当前分配给局部变量的对象,或者甚至从调用函数内部重新分配局部变量。

基本思想是拥有一个可以进行访问的函数,并且可以将其作为对象传递给其他函数或存储在类中。

一种方法是在包装函数中使用global(对于全局变量)或nonlocal(对于函数中的局部变量)。

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

相同的想法适用于读取和del设置变量。

对于正读,甚至有一种更短的使用方法,lambda: x该方法返回可调用对象,而被调用时将返回x的当前值。这有点像遥远的过去在语言中使用的“按名称呼叫”。

传递3个包装器来访问变量有点麻烦,因此可以将它们包装到具有proxy属性的类中:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

借助Python的“反射”支持,可以获得一个对象,该对象能够在给定范围内重新分配名称/变量,而无需在该范围内显式定义函数:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

在这里,ByRef该类包装了字典访问权限。因此,对属性的访问将wrapped转换为所传递字典中的项目访问。通过传递内建的结果locals和局部变量的名称,最终访问局部变量。从3.5版开始的python文档建议更改字典可能不起作用,但似乎对我有用。

While pass by reference is nothing that fits well into python and should be rarely used there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.

The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.

One way is to use global (for global variables) or nonlocal (for local variables in a function) in a wrapper function.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

The same idea works for reading and deleting a variable.

For just reading there is even a shorter way of just using lambda: x which returns a callable that when called returns the current value of x. This is somewhat like “call by name” used in languages in the distant past.

Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Pythons “reflection” support makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Here the ByRef class wraps a dictionary access. So attribute access to wrapped is translated to a item access in the passed dictionary. By passing the result of the builtin locals and the name of a local variable this ends up accessing a local variable. The python documentation as of 3.5 advises that changing the dictionary might not work but it seems to work for me.


回答 20

给定python处理值和对其的引用的方式,您可以引用任意实例属性的唯一方法是按名称进行:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

当然,在实际代码中,您将在dict查找上添加错误检查。

given the way python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

in real code you would, of course, add error checking on the dict lookup.


回答 21

由于字典是通过引用传递的,因此可以使用dict变量在其中存储任何引用的值。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

回答 22

Python中的按引用传递与C ++ / Java中的按引用传递概念完全不同。

  • Java&C#:原始类型(包括字符串)通过值(副本)传递,引用类型通过引用(地址副本)传递,因此对调用者可见的所有被调用函数中的参数更改。
  • C ++:允许按引用传递或按值传递。如果参数是通过引用传递的,则可以根据是否以const形式传递参数来对其进行修改。但是,无论是否为const,该参数都将保留对对象的引用,并且无法将引用分配为指向所调用函数内的另一个对象。
  • Python: Python是“按对象传递引用”,通常这样说:“对象引用按值传递。” [阅读此处] 1。调用者和函数都引用相同的对象,但是函数中的参数是一个新变量,它仅在调用者中保存对象的副本。像C ++一样,可以在函数中修改或不修改参数-这取决于传递的对象的类型。例如; 不变的对象类型不能在调用的函数中修改,而可变的对象可以更新或重新初始化。更新或重新分配/重新初始化可变变量之间的关键区别在于,更新后的值会反映在被调用函数中,而重新初始化后的值则不会。将新对象分配给可变变量的范围是python中函数的局部范围。@ blair-conrad提供的示例很好地理解了这一点。

Pass-By-Reference in Python is quite different from the concept of pass by reference in C++/Java.

  • Java&C#: primitive types(include string)pass by value(copy), Reference type is passed by reference(address copy) so all changes made in the parameter in the called function are visible to the caller.
  • C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.
  • Python: Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.”[Read here]1. Both the caller and the function refer to the same object but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function – This depends upon the type of object passed. eg; An immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized. A crucial difference between updating or re-assigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. Scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.

回答 23

由于您的示例碰巧是面向对象的,因此可以进行以下更改以获得相似的结果:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

Since your example happens to be object-oriented, you could make the following change to achieve a similar result:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

回答 24

您只能将一个空类用作存储参考对象的实例,因为内部对象属性存储在实例字典中。参见示例。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

You can merely use an empty class as an instance to store reference objects because internally object attributes are stored in an instance dictionary. See the example.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

回答 25

由于似乎没有地方提到,例如C ++等已知的模拟引用的方法是使用“更新”函数并传递该函数而不是实际变量(或更确切地说,是“名称”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

这对于“仅引用”或在具有多个线程/进程的情况下(通过使更新功能线程/多重处理安全)最有用。

显然,以上内容不允许读取值,而只能对其进行更新。

Since it seems to be nowhere mentioned an approach to simulate references as known from e.g. C++ is to use an “update” function and pass that instead of the actual variable (or rather, “name”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

This is mostly useful for “out-only references” or in a situation with multiple threads / processes (by making the update function thread / multiprocessing safe).

Obviously the above does not allow reading the value, only updating it.