Repository: qiwsir/StackOverFlowCn
Branch: master
Commit: ee9f7178ebbc
Files: 29
Total size: 80.1 KB
Directory structure:
gitextract_jkhcmjtr/
├── 101.md
├── 102.md
├── 103.md
├── 104.md
├── 105.md
├── 106.md
├── 107.md
├── 108.md
├── 109.md
├── 110.md
├── 111.md
├── 112.md
├── 113.md
├── 114.md
├── 115.md
├── 201.md
├── 202.md
├── 301.md
├── 302.md
├── 303.md
├── 304.md
├── 305.md
├── 306.md
├── 307.md
├── 308.md
├── 309.md
├── 401.md
├── 402.md
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: 101.md
================================================
#Python是否有三元条件运算符?
原问题地址:http://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator
##问题:
如果没有,是否可以使用其他语言结构来模拟一个?
##答案 1
是的,在2.5版本以后增加了。语法是:
a if test else b
首先对test进行评估,然后根据test的布尔值返回到a或b;
如果test评估结果为真,则返回到a,否则就返回到b。
例如:
>>> 'true' if True else 'false'
'true'
>>> 'true' if False else 'false'
'false'
记住,一些python开发者对此并不赞成:
- 参数的顺序不同于许多其他的语言(如:C, Ruby, Java等)。当人们不熟悉Python的“奇怪”行为时(他们可能会颠倒顺序),可能会导致错误。
- 有些人觉得它“不便使用”,因为它违背了常规思路:人们习惯先考虑条件,后考虑效果。
- 格式上的原因。
如果你在记忆顺序方面有困难(正如许多人似乎都有这个困难),那么请记住,把它大声读出来,你(几乎)是在说,`x = 4 if b > 8 else 9`被大声读作`x will be 4 if b is greater than 8 otherwise 9`(x就等于4,如果b大于8而不是9)。
##答案 2
模拟Python三元运算符
例如:
a, b, x, y = 1, 2, 'a greather than b', 'b greater than a'
result = (lambda:y, lambda:x)[a > b]()
输出:
'b greater than a'
-------
打赏帐号:qiwsir@126.com(支付宝),qiwsir(微信号)
================================================
FILE: 102.md
================================================
#如何把两个Python字典用一个表达式合并?
原问题地址:http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-in-a-single-expression
##问题:
我有两个Python字典,想写一个表达式,实现两个字典的合并。用`update()`方法,如果它能够返回合并结果而不是原地修改,就是我想要的。
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print z
None
>>> x
{'a': 1, 'b': 10, 'c': 11}
我怎样才能让最终合并后的字典是在z中,而不是在x中?(要格外清楚,最后一个成功解决dict.update()的冲突的方法,也是我所寻求的。)
##回答:
你有两个字典,你想把它们合并成一个新的字典,而又不改变原有字典中的内容:
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
y是后一个,它的值将取代x的值,因此'b'将在最终的结果中指向3。
经典的Python解法是分两步处理:
z = x.copy()
z.update(y)
[PEP 448](https://www.python.org/dev/peps/pep-0448)中提出的新语法在 [Python 3.5](https://mail.python.org/pipermail/python-dev/2015-February/138564.html)中可用。新语法是这样的
z = {**x, **y}
它满足了你所提出的要求(单一的表达式)。它符合[Python 3.5 PEP 478](https://www.python.org/dev/peps/pep-0478/#features-for-3-5),并且它已经出现在文档[What's New in Python 3.5](https://docs.python.org/dev/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations)中。
然而,由于许多组织仍然在使用Python 2,在未来几年的时间里,新语法都不太可能在生产环境中被使用。
###在Python 2中,合并字典的表达式
随着许多组织把Python版本升级到Python 3,Python 3.5中提出的新的解决方案将会成为这个问题的主要解决方案。
然而,如果你还没有使用Python 3.5,并且你还想在一个表达式中实现这个功能,最高效的方法就是把它放在一个函数中:
def merge_two_dicts(x, y):
'''Given two dicts, merge them into a new dict as a shallow copy.'''
z = x.copy()
z.update(y)
return z
然后,你就有一个单一的表达式:
z = merge_two_dicts(x, y)
你也可以创建一个函数来合并一些数量不确定的字典,从零到一个非常大的数量:
def merge_dicts(*dict_args):
'''
Given any number of dicts, shallow copy and merge into a new dict, precedence goes to key value pairs in latter dicts.
'''
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
这个函数对于Python 2和Python 3中所有的字典都适用。例如,在给定的从a到g的字典中,
z = merge_dicts(a, b, c, d, e, f, g)
字典g中的键值对优先于字典a到f,依此类推(即“最后一个有效”——译者注)。
###对其他答案的评论
不要采用你所看到的投票最多的答案:
z = dict(x.items() + y.items())
在Python 2中,你在内存中为这两个字典创建两个列表,创建的第三个列表的长度等于前两个列表加起来的长度,然后创建字典并丢弃所有这三个列表。在Python 3中,这样做就行不通了,因为你是把两个dict_items加在一起,而不是两个列表。
>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
and you would have to explicitly create them as lists, e.g. z = dict(list(x.items()) + list(y.items())). This is a waste of resources and computation power.
你必须明确地把它们作为列表来创建,例如`z = dict(list(x.items()) + list(y.items()))`。这是对资源和计算能力的一种浪费。
同样地,如果值不是可哈希对象(例如:列表),在Python 3 中合并items()也不会成功(Python 2.7中viewitems())。即使你的值是可哈希的,因为字典是无序的,也没有定义行为的优先顺序。所以不要这样做:
>>> c = dict(a.items() | b.items())
这个例子说明了如果值不是哈希表时会发生什么情况:
>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
这里有一个例子,它说明了y在什么情况下应该有优先权,但是由于字典的顺序是任意的,x的值被保留下来:
>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}
你不应该使用的另一个把戏:
z = dict(x, **y)
这里使用了字典构造器,并且速度非常快、内存效率高(甚至略高于我们的两步法)。但是这个把戏很难读懂,除非你确切地知道这里发生了什么情况(第二个字典被作为关键字参数传递给字典构造器),这不是预期的用法,所以不Python。另外,当关键字不是字符串时,这种方法在Python 3中也行不通。
>>> c = dict(a, **b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings
在[邮件列表](https://mail.python.org/pipermail/python-dev/2010-April/099459.html)中,吉多·范罗苏姆,Python语言的创造者,写到:
>“I am fine with declaring `dict({}, **{1:3})` illegal, since after all it is abuse of the `**` mechanism.”
>“我可以宣布`dict({}, **{1:3})`是非法的,因为这毕竟是在滥用`**`机制。”
>“Apparently dict(x, **y) is going around as "cool hack" for "call x.update(y) and return x". Personally I find it more despicable than cool.”
>“显然,`dict(x, **y)`是对于`x.update(y) and return x`的“酷黑”。我个人觉得它不是‘酷’而是卑劣。”
###正确但性能不高的点对点模式
这些方法的性能不高,但它们将提供正确的功能,性能会比copy和update或新的unpacking低得多,因为它们在更高的抽象层次上遍历每个键值对,但它们肯定尊重优先顺序(后面的字典优先)
你也可以在字典的解析式中创建字典:
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
或者在Python 2.6中(也许早在引入了生成器表达式的Python2.4中):
dict((k, v) for d in dicts for k, v in d.items())
itertools.chain将迭代器的键值对按正确的顺序链接起来:
import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))
###性能分析
我只对能够正确运行的那些用法进行性能分析。
import timeit
以下适用于Ubuntu 14.04和Python 2.7(Python系统):
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934
Python 3.5:
>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287
-------
打赏帐号:qiwsir@126.com(支付宝)
================================================
FILE: 103.md
================================================
#对Python字典按值排序
原问题地址:http://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value
##问题:
我有一个值的字典,它的值来自于数据库中的两个字段:字符串字段和数字字段。字符串字段是独一无二的,字典的键也是如此。
我可以按键排序,但我怎么才能按值排序?
注:我读了Stack Overflow的问题:[我如何在Python中对字典列表按值排序?](http://stackoverflow.com/questions/72899/how-do-i-sort-a-list-of-dictionaries-by-values-of-the-dictionary-in-python),我也许能够改变我的代码来获取字典列表,但我真的不需要字典列表,我想知道是否有一个更简单的解决方案。
##答案 1:
对一个字典排序,却只得到这个经过排序的字典,这是不可能的。字典本质上是无序的,但其他类型,如列表和元组,却是有序的。所以你需要一个排序表示法,也就是一个列表,很可能是元组的列表。
例如,
import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))
sorted_x将会是元组的列表,这个列表根据每个元组中的第二个元素来排序。dict(sorted_x) == x。
如果你希望根据键而不是根据值进行排序:
import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))
##答案 2:
你可以使用:
sorted(d.items(), key=lambda x: x[1])
这是按照字典中每个条目的值从小到大的顺序进行排序。
##答案 3:
字典不可以排序,但是你可以为它们建立一个有序的列表。
按照字典的值排序后的列表:
sorted(d.values())
按值排序的(键、值)对的列表:
from operator import itemgetter
sorted(d.items(), key=itemgetter(1))
================================================
FILE: 104.md
================================================
#append和extend的区别
原问题地址:http://stackoverflow.com/questions/252703/python-append-vs-extend
##问题
列表的两个方法`append()`和`extend()`的区别是什么?
##答案:
[append](https://docs.python.org/2/library/array.html?#array.array.append):在末尾添加对象
x = [1, 2, 3]
x.append([4, 5])
print (x)
得出:`[1, 2, 3, [4, 5]]`
[extend](https://docs.python.org/2/library/array.html?#array.array.extend):通过添加可迭代对象的元素来扩展列表
x = [1, 2, 3]
x.extend([4, 5])
print (x)
得出: `[1, 2, 3, 4, 5]`
================================================
FILE: 105.md
================================================
#获得Python的循环索引
原问题地址:http://stackoverflow.com/questions/522563/accessing-the-index-in-python-for-loops
##问题:
是否有人知道如何得到列表自身的索引,比如:
ints = [8, 23, 45, 12, 78]
当我使用for循环这个列表的时候,如何得到从1到5的索引?
##答案:
使用额外的状态变量,如索引变量(你在C语言或PHP语言中通常会用到它),这被认为不是Python的风格。
更好的选择是使用Python 2和Python 3中的内建函数`[enumerate()](https://docs.python.org/2/library/functions.html#enumerate)`:
for idx, val in enumerate(ints):
print idx, val
更多信息请查看[PEP 279](https://www.python.org/dev/peps/pep-0279/)。
================================================
FILE: 106.md
================================================
#判断一个字符串是否含子串的方法
原问题地址:http://stackoverflow.com/questions/3437059/does-python-have-a-string-contains-substring-method
##问题:
我在Python中查找`string.contains`或`string.indexof`方法。
我想实现:
if not somestring.contains("blah"):
continue
##回答 1:
你可以使用[in](https://docs.python.org/reference/expressions.html#membership-test-details):
if "blah" not in somestring:
continue
##回答 2:
如果你只是搜索一个子字符串,可以使用`string.find("substring")`。
然而,你使用`find`,`index` 和 `in`的时候一定要小心一点,因为这是在搜索子字符串。换句话说,下面的内容:
s = "This be a string"
if s.find("is") == -1:
print "No 'is' here!"
else:
print "Found 'is' in the string."
打印结果是:`Found 'is' in the string`。类似于`if "is" in s:`。
这可能是你想要的,也可能不是。
================================================
FILE: 107.md
================================================
#怎样在Python中实现Enum(枚举)的功能
原问题地址:http://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python
##问题:
我是一个C#开发者,但目前致力于Python的一个项目。
我怎样在Python中实现类似于Enum(枚举)的功能?
##回答:
根据 [PEP 435](http://www.python.org/dev/peps/pep-0435/)中的描述,Enums已被添加到Python 3.4 中。在[Python3.3,3.2,3.1,2.7,2.6,2.5和2.4的pypi中也增添了Enums](https://pypi.python.org/pypi/enum34)。
对于更高级的Enum技术,请访问[aenum library](https://pypi.python.org/pypi/aenum)(Python 2.7,3.3+,和enum34是同一作者)。
- 使用enum34,安装方法`$ pip install enum34`
- 使用aenum, 安装方法 `$ pip install aenum`
安装enum(无编号)将得到一个完全不同的、不兼容的版本。
from enum import Enum # for enum34, or the stdlib version
# from aenum import Enum # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')
或者相当于:
class Animal(Enum):
ant = 1
bee = 2
cat = 3
dog = 4
在早期版本中,实现enums的一种方法是:
def enum(**enums):
return type('Enum', (), enums)
它是这样运行的:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
你还可以用这样的代码很容易地支持自动枚举:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
它是这样运行的:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
你可以用这种方法把值转换为名称:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = dict((value, key) for key, value in enums.iteritems())
enums['reverse_mapping'] = reverse
return type('Enum', (), enums)
这样做将覆盖任何带有该名字的内容,但是它有助于在输出时渲染你的enums(枚举)。如果反向映射不存在,它将放弃KeyError。下面看第一个例子:
>>> Numbers.reverse_mapping['three']
'THREE'
================================================
FILE: 108.md
================================================
#将多行的异常变成一行
原问题地址:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block
##问题:
我知道我可以这样做:
try:
# do something that may fail
except:
# do this if ANYTHING goes wrong
我也可以这样做:
try:
# do something that may fail
except IDontLikeYourFaceException:
# put on makeup or smile
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
所以,我为了捕捉到这两种不同的异常所做的努力并没有奏效。
有办法成功做到吗?
##回答:
用圆括号括起来
except (IDontLIkeYouException, YouAreBeingMeanException) as e:
pass
在Python 2.6和Python2.7中仍然可以用逗号把异常分开,但现在这样做已经过时了,不再适用于现在所使用的Python 3。现在你应该使用`as`。
================================================
FILE: 109.md
================================================
#解释Python的切片法
原问题地址:
http://stackoverflow.com/questions/509211/explain-pythons-slice-notation
##问题:
我需要关于Python切片明确说明(包括参考文献)。
对我来说,切片问题有点费神。它看起来非常强大,但我还没有完全搞清楚。
##回答:
Python切片很简单:
a[start:end] # items start through end-1
a[start:] # items start through the rest of the array
a[:end] # items from the beginning through end-1
a[:] # a copy of the whole array
还有步长,它可以用在上面任何一处中:
a[start:end:step] # start through not past end, by step
要记住的关键点是:`:end`中的`end`所表示的值表示所选定的切片之后的第一值【译者注:也就是`end`的值所对应的字符,没有包括在所选定的切片之中。可以概括为“前包括,后不包括”的范围选择原则。】所以,结束和起始的区别是索引是否被乃入切片范围(如果步长是默认值1)。
另一个特点是,起始或结束可能是负数,这就意味着它是从数组的末尾开始计数,而不是起始处开始计数。所以:
a[-1] # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items
如果实际元素少于所提交的元素个数,Python会给出温和的反馈。例如,如果你执行`a[:-2]`,而`a`只包含一个元素,反馈给你的就是空的列表,而不是错误列表。有时候,你更愿意得到错误提示,所以你一定要知道出现这种情况的可能性。
================================================
FILE: 110.md
================================================
#如何在Python中获取当前时间
原问题地址:http://stackoverflow.com/questions/415511/how-to-get-current-time-in-python
##问题:
获取当前时间所用的模块/方法是什么?
##回答:
>>> import datetime
>>> datetime.datetime.now()
datetime(2009, 1, 6, 15, 8, 24, 78915)
仅是时间:
>>> datetime.datetime.time(datetime.datetime.now())
datetime.time(15, 8, 24, 78915)
另一种:
>>> datetime.datetime.now().time()
阅读此[文档](https://docs.python.org/2/library/datetime.html)获取更多信息。
为了免去打字的麻烦,你可以从datetime模块引入datetime对象:
>>> from datetime import datetime
然后删除前面所有前置的datetime。
================================================
FILE: 111.md
================================================
#查看某个给定的键是否已经在字典中
原问题地址:http://stackoverflow.com/questions/1602934/check-if-a-given-key-already-exists-in-a-dictionary
##问题:
更新某键的值以前,我想先知道它是否存在于字典中。我写了以下代码:
if 'key1' in dict.keys():
print "blah"
else:
print "boo"
我认为这不是完成此任务的最佳方法。有没有更好的方法来知晓字典中的某个键?
##回答:
`in`就是用于探知字典中是否存在某个键的一种方式
d = dict()
for i in xrange(100):
key = i % 10
if key in d:
d[key] += 1
else:
d[key] = 1
如果你想要一个默认值,可以使用`dict.get()`:
d = dict()
for i in xrange(100):
key = i % 10
d[key] = d.get(key, 0) + 1
如果你想一直确保某个键的默认值,可以使用collections模块中的defaultdict,就像下面这样:
from collections import defaultdict
d = defaultdict(lambda: 0)
for i in xrange(100):
d[i % 10] += 1
但一般说来,关键字in是用于探知字典中是否存在某个键的最佳方法。
================================================
FILE: 112.md
================================================
#如何删除Python中的换行符?
原问题地址:
http://stackoverflow.com/questions/275018/how-can-i-remove-chomp-a-newline-in-python
##问题:
在Python中,什么是与Perl 的`chomp` 功能相对应,用于消除某个值的最后一个字符?
回答:
尝试`rstrip`方法。
>>> 'test string\n'.rstrip()
'test string'
注意Python中的rstrip方法是以默认的方式除去各种尾部空格,而不只是像Perl中的chomp那样除去换行符。为了达到只除去换行符的目的:
>>> 'test string \n'.rstrip('\n')
'test string '
你还可以使用lstrip和strip方法。
>>> s = " \n abc def "
>>> s.strip()
'abc def'
>>> s.rstrip()
' \n abc def'
>>> s.lstrip()
'abc def '
>>>
================================================
FILE: 113.md
================================================
#如何将Python列表均匀划分?
原问题地址:http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
##问题:
我有一个任意长度的列表,我需要把它拆分成长度相等的更小的数据集,然后进行操作。有一些平淡无奇的方法可以实现这一需求,比如:保存一个计数器和两个列表,当第二个列表填满时,将第二个列表添加到第一个列表,然后清空第二个列表以便填充下一轮数据,但这样做的代价可能是非常昂贵的。
我想知道是否有人想出了一个很好的解决方案,使之适用于于任何长度的列表,例如;使用生成器。
这个方案应该能够奏效:
l = range(1, 1000)
print chunks(l, 10) -> [ [ 1..10 ], [ 11..20 ], .., [ 991..999 ] ]
我曾经在itertools中寻找一些有用的东西,但我找不到任何明显有用的东西。然而,我可能错过了它。
##回答:
这是一个生成器,它可以生成你想要的数据集:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i+n]
import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
如果你正在使用Python 2,你应该使用`xrange()`来代替`range()`:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in xrange(0, len(l), n):
yield l[i:i+n]
你也可以简单地使用列表解析来代替函数的书写。
Python 3:
[l[i:i+n] for i in range(0, len(l), n)]
Python2:
[l[i:i+n] for i in xrange(0, len(l), n)]
================================================
FILE: 114.md
================================================
#Python中的join函数为什么是string.join(list)而不是list.join(string)?
原问题地址:http://stackoverflow.com/questions/493819/python-join-why-is-it-string-joinlist-instead-of-list-joinstring
##问题:
这个问题一直困扰着我。这样的书写看起来
my_list = ["Hello", "world"]
print my_list.join("-")
# Produce: "Hello-world"
胜过这样的书写:
my_list = ["Hello", "world"]
print "-".join(my_list)
# Produce: "Hello-world"
这里面是否有个特殊的原因呢?
##回答:
这是因为可以被加进来的不只是列表,还可以是任何一个可迭代对象,但结果和连接符始终都是字符串。
例如:
import urllib2
print '\n############\n'.join(
urllib2.urlopen('http://data.stackexchange.com/users/7095'))
================================================
FILE: 115.md
================================================
#我如何在Python中检查一个字符串否可转化为数字?
原问题地址:http://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float-in-python
##问题:
在Python中,检查一个字符串是否可以表示为数字的最可行的办法是什么?
我现在所使用的函数是:
def is_number(s):
try:
float(s)
return True
except ValueError:
return False
这个函数不仅丑陋、缓慢,似乎还很笨拙。但是我还没有找到一个更好的方法,在应用中直接使用float()更差劲了。
##回答:
我只谈两点。
一个正则表达式或其他字符串解析将会更加丑陋和缓慢。
我不知道有什么函数比上面的函数更快。它调用函数并返回。Try/Catch不会招致太多代价,因为我们所遇到的最常见的例外也无需扩展搜索堆栈帧。
问题是任何数字转换函数都有两种结果
- 数字,如果这个数字是有效的
- 代码状态(例如,通过errno)或异常,显示没有有效的数字可以解析。
C语言(举个例子)围绕这个问题提出了许多方法。Python提供了清晰、明确的表达。
我认为你的代码完美地解决了这个问题。
================================================
FILE: 201.md
================================================
#如何使用Python来检查文件是否存在?
原问题地址:http://stackoverflow.com/questions/82831/how-to-check-whether-a-file-exists-using-python
##问题:
我如何使用Python而不使用`try-catch`语句来检查文件是否存在?
##答案 1
你也可以使用`os.path.isfile`
如果是一个现有的常规文件,就返回到True。这是符号链接,所以`islink()`和`isfile()`可以适用于相同的路径。
import os.path
os.path.isfile(fname)
如果你需要确保它是一个文件。
##答案 2:
用`os.path.exists`:
import os.path
os.path.exists(file_path)
如果是文件和目录,都会返回到True。但是你还可以使用`os.path.isfile`来专门查看它是否是一个文件。
-------
打赏帐号:qiwsir@126.com(支付宝),qiwsir(微信号)
================================================
FILE: 202.md
================================================
#如何在Python中列出目录下的所有文件
原问题地址:http://stackoverflow.com/questions/3207219/how-to-list-all-files-of-a-directory-in-python
##问题:
我如何在Python中列出目录下的所有文件并将它们添加到一个列表?
##回答:
使用[os.listdir()](https://docs.python.org/2/library/os.html#os.listdir)可以获取目录中的所有内容 —— 文件和子目录。
如果你只想获取文件,你可以使用`os.path`进行过滤
from os import listdir
from os.path import isfile, join
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
或者可以使用[os.walk()](https://docs.python.org/2/library/os.html#os.walk)。你所访问的每个目录都将生成两个列表 —— 文件列表和目录列表。如果你想要的只是顶层目录,你可以在目录列表第一次生成的时候就结束它。
from os import walk
f = []
for (dirpath, dirnames, filenames) in walk(mypath):
f.extend(filenames)
break
最后,正如上面这个例子所表明的那样,要把一个列表添加到另一个列表,你可以使用[.extend()](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists)或
>>> q = [1, 2, 3]
>>> w = [4, 5, 6]
>>> q = q + w
>>> q
[1, 2, 3, 4, 5, 6]
就我个人而言,我更喜欢`.extend()`
================================================
FILE: 301.md
================================================
#Python中yield关键词的作用是什么?
原问题地址:http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python
##问题:
在Python中yield关键字的作用是什么?它是做什么用的?
例如,我正试图理解这段代码:
def node._get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
这是调用代码:
result, candidates = list(), [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
当调用`_get_child_candidates`方法的时候发生了什么呢?返回某一个列表?返回某一个元素?它是不是再次被调用了?后续调用什么时候才会停下来?
注:这里的代码是Jochen Schulz(jrschulz)写的。他建立了一个很棒的度量空间的Python库。点击这个链接就可以看到完整的源代码:[Module mspace](http://well-adjusted.de/~jrschulz/mspace/)。
##答案 1:
要了解yield是做什么用的,你就必须了解什么是生成器。在了解生成器之前,先要了解可迭代。
###可迭代
当你创建一个列表后,可以逐个读取它的元素。对一个列表的逐个读取被称为迭代。
>>> for i in mylist:
... print(i)
1
2
3
mylist就是可迭代的。当使用列表解析时,你就创建了一个列表,也就是一个可迭代对象:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
所有可以使用"for... in..."的对象都是可迭代的,如:列表, 字符串, 文件…
这些可迭代对象使用起来很方便,因为你可以随心所欲。但是所有的值都存在了内存中,当有大量数据的时候,这就不是你想要的结果了。
###生成器
生成器是迭代器,但只能遍历一次。这是因为并非所有的值都在内存中,它们被实时生成:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
除非你用`()`来代替`[]`,否则上面两段代码的效果是一样的。但是,你不能再次运行`for i in mygenerator`,因为生成器只能使用一次:他们计算0,然后忘掉它(不保存)并计算1,最后计算4,一个接一个地进行。
###Yield
Yield是一个关键词,它的用法就像return,只不过它返回一个生成器。
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
这段代码示例没什么用处,但是当你知道函数要返回大量的并且仅需要读一次的数据时,就会觉得它是方便的。
要掌握yield,你就必须明白,当你调用这个函数时,写在函数体里的代码并没有运行。该函数只返回到生成器对象,这可有点棘手。
接下来,每当for使用生成器的时候,你的代码就会被运行。
现在的困难部分是:
第一次用for调用你的函数所创建的生成器对象时,它将会从头开始运行函数中的代码,直到遇到yield,才会返回该循环中的第一个值。然后,每次调用都会再运行一次函数中你所写的循环,并返回下一个值,直到没有什么值可以返回。
一旦函数在运行过程中不再遇到yield,生成器就会被认为是空的。这可能是因为循环已经结束,或者因为你不再满足"if/else"条件了。
###代码说明
生成器:
#这里是你创建node对象的方法,它将返回生成器
def node._get_child_candidates(self, distance, min_dist, max_dist):
#这里是你每次使用生成器对象时所调用的代码:
#如果它的左边仍有一个节点对象的子节点
#如果距离尚可,返回下一个子节点
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
#如果它的右边仍有一个节点对象的子节点
#如果距离尚可,返回下一个子节点
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
#如果函数到达这里,生成器将被视为空
#这里只有两个值:左边的子节点、右边的子节点
调用代码:
#创建一个空的列表和一个具有当前对象引用的列表
result, candidates = list(), [self]
#候选项的循环(它们一开始只包含一个元素)
while candidates:
#找到最后的候选项并且从列表中删除它
node = candidates.pop()
#获取obj(当前对象)和候选项之间的距离
distance = node._get_dist(obj)
#如果距离尚可,那么你可以填写结果
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
#在候选项名单中添加候选项的子项
#这样一来,该循环将继续运行直到
#所有候选项的子项的子项都运行完毕
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
返回结果
此代码包含几个巧妙的部分:
- 该循环迭代一个列表,但在迭代过程中列表会扩大。这是运行所有的嵌套数据的一个简洁的方法。只不过这样做有点危险,因为你最终可能会遇到死循环。在这个示例中,`candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))`会读取这个生成器的所有的值,但while循环不断创造新的生成器对象,它们会从以前的生成器对象中产生出不同的值,因为所应用的节点不同。
- 列表的`extend()`方法,允许可迭代对象并把值添加到列表中。
通常我们给`extend()`方法传一个列表:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
但在代码中,会接收生成器,这是好现象,因为:
1. 你不需要把这些重复读取。
2. 你可以有很多子节点,并且不希望把它们都存在内存中。
它之所以奏效是因为Python不在乎一种方法的参数是不是一个列表。Python允许迭代,因此它可以使用字符串、列表、元组和生成器。这就是所谓的鸭子类型,这也是Python很酷的原因之一。但这是另一个故事了,另一个问题…
你可以停在这里,或者读一点关于生成器的高级使用方法:
###控制生成器的损耗
>>> class Bank(): # let's create a bank, building ATMs
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
它可以有利于各种事情,如:控制对于资源的访问。
###迭代工具,你最好的朋友
`itertools`模块有一些处理可迭代对象的专用方法。你是否曾经希望复制一个生成器?链接两个生成器?用一行代码把嵌套列表中的值进行分组?在无需创建另一个列表进行`Map/Zip`?
那么,`import itertools`吧。
一个例子,让我们看看4匹赛马的抵达顺序:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
###理解迭代的内在机制
迭代是一个过程,必然包括可迭代对象(实现`__iter__()`方法)和迭代器(实现`__next__()`方法)。可迭代对象是可以获得迭代器的任何对象。迭代器允许你对可迭代对象进行迭代。
##答案 2
###深刻理解yield的捷径
(关于[Grokking](https://en.wikipedia.org/wiki/Grok))
当你看到一个用yield语句的函数时,应用这个简单的技巧来了解将会发生的情况:
1. 在函数的开始处插入一行`result = []`。
2. 把每一处yield语句替换为`result.append(expr)`。
3. 在函数的末端插入一行`return result`。
4. 耶!再也没有用yield语句了。阅读并理解代码。
5. 把这个函数与初始定义进行比较。
这个技巧可能会使你对函数背后的逻辑有所了解,但使用yield和使用列表的函数执行起来是有着显著的不同。在很多情况下,使用yield会使得内存效率更高、更快。在其他情况下,即使原函数运行良好,这个技巧也会使你陷入死循环中。继续阅读以便加深了解…
###不要把你的可迭代对象、迭代器和生成器弄混
首先,迭代器协议。当你书写下面内容的时候
for x in mylist:
...loop body...
Python执行以下两个步骤:
1. 获取mylist的迭代器:调用`iter(mylist)`->返回一个带有`next()`方法的对象(或者在Python 3中返回`__next__()`)。(大多数人忘记告诉你这一步)
2. 循环遍历迭代器:继续调用在步骤1返回的迭代器的`next()`方法。`next()`的返回值被赋给`x`,并且循环体也在运行。如果在`next()`中抛出`StopIteration`异常,就意味着在迭代器中没有更多的值了,循环结束了。
事实上,每当Python需要循环遍历某对象的内容时,它就会执行上述两个步骤。所以它可能是一个for循环,但它也可能是像`otherlist.extend(mylist)`这样的代码(其中otherlist是一个Python列表)。
这里的mylist是一个可迭代对象,因为它执行了迭代器协议。在一个自定义的类中,你可以使用`__iter__()`方法来让你的实例成为可迭代对象。此方法应该返回到一个迭代器。迭代器是含有`next()`方法的对象。可以在同一个类中执行`__iter__()`和`next()`,并让`__iter__()`结果返回self。上述做法只适用于简单的情况。当你想要让两个迭代器在同一时间对同一对象进行循环的时候,这个做法就不能奏效了。
这就是迭代器协议,许多对象都执行这个协议:
1. 列表、字典、元组、集合、文件。
2. 自定义类中的`__iter__()`。
3. 生成器。
注意,for循环不知道它所处理的是什么类型的对象,它只是遵循了迭代器协议,并且只要是调用了`next()`,它就会遍历所有内容。列表会一个一个地返回到其元素,字典一个一个地返回键,文件一行一行地返回每行内容,生成器返回…这就是yield起作用的地方:
def f123():
yield 1
yield 2
yield 3
for item in f123():
print item
如果你在`f123()`中没有使用yield语句,而是使用了三个return 语句,那么只有第一个会被执行,函数将会退出。但`f123()`不是普通的函数。当你调用`f123()`的时候,它不会到返回到yield语句中的任何一个值!它返回到一个生成器对象。此外,该函数并没有真正退出,它进入了一个挂起(暂停)状态。当for 循环尝试对生成器对象进行循环时,该函数将从它的挂起状态恢复运行,直到下一个yield语句,并同前运行。这种情况一直持续到函数退出为止,最后,生成器就会抛出`StopIteration`异常,循环也随之结束。
因此,生成器对象有点儿像适配器。它的一端展示了迭代器协议,利用`__iter__()`和`next()`方法保持for循环的畅通。然而,在另一端,它所运行的函数只能够支持它获取下一个值,并把它返回到挂起模式。
###为什么要使用生成器?
通常,你在写代码时,不使用生成器也能实现相同的逻辑。一个选择是使用我之前提到过的临时列表的“把戏”。这种做法并非在所有的情况下都适用。例如,如果你遇到了死循环,或者当你有一个很长的列表时,它可能会导致内存使用效率低下。另一种方法是使用一个新的可迭代的类型。`SomethingIter`适用于实例成员,并在它的`next()` 或者Python 3中的`__next__()`方法中执行下一个逻辑步骤。根据逻辑,在`next()`方法内部的代码可能看起来很复杂,也容易出现错误。生成器可以为这种情况提供一个干净且容易的解决方案。
-------
打赏帐号:qiwsir@126.com(支付宝),qiwsir(微信号)
================================================
FILE: 302.md
================================================
#Python中的元类是什么?
原问题地址:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
##问题:
什么是元类?使用它们能做什么?
##答案 1
元类是类的一种。正如类定义了实例功能,元类也定义了类的功能。类是元类的实例。
在Python中你可以随意调用元类(参考[Jerub的回答](http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python/100037#100037)),实际上更有用的方法是使其本身成为一个真正的类。type是Python中常用的元类。你可能觉得奇怪,是的,type本身就是一个类,而且它就是type类型。你无法在Python中重新创造出完全像type这样的东西,但Python提供了一个小把戏。你只需要把type作为子类,就可以在Python中创建自己的元类。
元类是最常用的一类工厂。就像你通过调用类来创建类实例,Python也是通过调用元类来创建一个新类(当它执行`class`语句的时候),结合常规的 `__init__`和`__new__`方法,元类可以允许你在创建类的时候做一些“额外的事情”,like registering the new class with some registry(暂时不知道这句话的含义,不知道怎么翻译,字面意思是:就像用某个注册表来注册新的类那样),甚至可以用别的东西完全代替已有的类。
当Python执行`class`语句时,它首先把整个的class语句作为一个正常的代码块来执行。由此产生的命名空间(一个字典)具有待定类的属性。元类取决于待定类的基类(元类是具有继承性的)、或待定类的`__metaclass__`属性(如果有的话)或`__metaclass__`全局变量。接下来,用类的名称、基类和属性调用元类,从而把元类实例化。
然而,元类实际上定义的一个类的类型,而不只是类工厂,所以你可以用元类来做更多的事情。例如,你可以定义元类的一般方法。这些元类方法和类方法有相似之处,因为它们可以被没有实例化的类调用。但这些元类方法和类方法也有不同之处,元类方法不能在类的实例中被调用。`type.__subclasses__()`是关于type的一个方法。你也可以定义常规的“魔法”函数,如`__add__`, `__iter__`和`__getattr__`,以便实现或修改类的功能。
摘抄一个例子:
def make_hook(f):
"""Decorator to turn 'foo' method into '__foo__'"""
f.is_hook = 1
return f
class MyType(type):
def __new__(cls, name, bases, attrs):
if name.startswith('None'):
return None
# Go over attributes and see if they should be renamed.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_hook', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(MyType, cls).__new__(cls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Would register class %s now." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Alternatively, to autogenerate the classname as well as the class:
# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "Would unregister class %s now." % self
class MyObject:
__metaclass__ = MyType
class NoneSample(MyObject):
pass
# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)
class Example(MyObject):
def __init__(self, value):
self.value = value
@make_hook
def add(self, other):
return self.__class__(self.value + other.value)
# Will unregister the class
Example.unregister()
inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()
print inst + inst
class Sibling(MyObject):
pass
ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
shareedit
##答案 2
###作为对象的类
在理解元类之前,你需要掌握Python中的类。Python对于类的定义很特别,这是从Smalltalk语言中借鉴来的。
在大多数语言中,类只是描述如何创建一个对象的代码段。Python中的类大体上也是如此:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
而Python中的类并不是仅限于此。它的类也是对象。
是的,对象。
当你使用关键字class时,Python执行它并创建一个对象。下面是有关的指令
>>> class ObjectCreator(object):
... pass
...
这个对象(类)本身就能够创建一些对象(实例),这就是为什么它是类。
但它仍然是一个对象,因而:
- 你可以将它分配给一个变量
- 你可以复制它
- 你可以增加它的属性
- 你可以把它作为一个功能参数来用
例如:
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
Creating classes dynamically
###类的动态创建
既然类是对象,你就能动态地创建它们,就像创建任何对象那样。
首先,你可以在一个使用class的函数中创建类:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
但它并不是动态的,因为你还是要自己写出整个类。
类是对象,它们必须由某种东西产生。
当你使用关键字class时,Python会自动创建该对象。但是,与Python中的大多数东西一样,它给你提供了一个手工操作的方法。
还记得type函数吗?这个一个好用的旧函数,它能让你了解一个对象的类型:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
type有着完全不同的能力,它还可以动态地创建类。type可以把对于类的描述作为参数,并返回一个类。
(同样的函数根据你传入的参数而有完全不同的用途。我知道这看起来有点怪。这是因为Python向后兼容。)
type是这样应用的:
例如:
>>> class MyShinyClass(object):
... pass
可以这样子来进行手动创建
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
你会注意到,我们使用“MyShinyClass”作为类的名称,并把它作为类所引用的变量。他们可以是不同的,但没有理由把事情复杂化。
type接受字典对于类属性的定义。所以:
>>> class Foo(object):
... bar = True
可以被转化为:
>>> Foo = type('Foo', (), {'bar':True})
并且被用作一个常规的类:
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True
当然,你也可以继承它,即:
>>> class FooChild(Foo):
... pass
可以转化为:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True
最终,你会想把一些方法添加到你的类中。需要用一个适当的识别标志来定义一个函数,并将其指定为一个属性。
>>> def echo_bar(self):
... print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True
你现在明白了:在Python中,类就是对象,你可以动态地创建类。
这就是关键字class在Python中的应用,它是通过使用元类来发挥作用。
###元类是什么
元类是用来创建类的东西。
你通过定义类来创建对象,对吧?
但我们知道Python的类就是对象。
元类用于创建这些对象,元类是类的类,你可以这样来描述它们:
MyClass = MetaClass()
MyObject = MyClass()
你已经看到了type可以这样来用:
MyClass = type('MyClass', (), {})
这是因为type实际上是一个元类,作为元类的type在Python中被用于在后台创建所有的类。
现在你感到疑惑的是为什么这里是小写的type,而不是大写的Type?
我想这是为了与创建字符串对象的类str和创建整数对象的类int在写法上保持一致。type只是用于创建类的类。
你可以通过检查`__class__`的属性来看清楚。
Python中的一切,我的意思是所有的东西,都是对象。包括整数、字符串、函数和类。所有这些都是对象。所有这些都是由一个类创建的:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
现在,任何`__class__`中的特定`__class__`是什么?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
所以,元类只是用于创建类对象的东西。
如果你愿意,你可以把它称为“类工厂”。
type是Python中内建元类,当然,你也可以创建你自己的元类。
### `__metaclass__`的属性
当你创建类的时候,可以添加一个`__metaclass__`属性:
class Foo(object):
__metaclass__ = something...
[...]
如果你这样做,Python会使用元类来创建Foo这个类。
小心,这是棘手的。
这是你是首次创建`class Foo(object)`,但是类对象Foo在内存中还没有被创建。
Python会在类定义中寻找`__metaclass__`。如果找到它,Python会用它来创建对象类Foo。如果没有找到它,Python将使用type来创建这个类。
把上面的话读几遍。
当你写下:
class Foo(Bar):
pass
Python会实现以下功能:
Foo有没有`__metaclass__`的属性?
如果有,通过借鉴`__metaclass__`,用Foo这个名字在内存中创建一个类对象(我说的是一个类对象,记住我的话)。
如果Python找不到`__metaclass__`,它会在模块层级寻找`__metaclass__`,并尝试做同样的事情(但这只适用于不继承任何东西的类,基本上是旧式类)。
如果它根本找不到任何`__metaclass__`,它将使用Bar(第一个父类)自己的元类(这可能是默认的type)来创建类对象。
小心点,`__metaclass__`属性不会被继承,而父类的元类(`Bar.__class__`)将会被继承。如果Bar所用的`__metaclass__`属性是用`type()`来创建Bar(而不是`type.__new__()`),它的子类不会继承这种功能。
现在最大的问题是,你可以在`__metaclass__`中写些什么?
答案是:可以创建类的东西。
什么可以创建类?type,或者父类。
###自定义元类
一个元类的主要目的是当它被创建时,这个类可以自动改变。
通常在API中,可以创建一个元类,以之匹配于当前的内容。
想象一个愚蠢的例子:模块中的所有类的属性都应该用大写字母来写。你有几种方法,其中的一种方法就是在模块层次上设置`__metaclass__`。
这样,这个模块中所有的类都将使用这个元类来创建,我们只需要告诉元类把所有属性改为大写。
幸运的是,`__metaclass__`实际上可以任意调用,它并不需要成为一个正式的类(我知道,名字中带有“class”字样的东西未必就是类,想想看吧…但这是有益的)。
因此,我们将通过使用函数来举一个简单的例子。
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True
f = Foo()
print(f.BAR)
# Out: 'bip'
现在,让我们完全照做,但使用一个真的类作为元类:
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)
但这不是真正的面向对象编程。我们直接调用type,我们无需覆盖或调用父类`__new__`。让我们着手吧:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
你可能已经注意到额外的参数`upperattr_metaclass`,它没有什么特别之处:`__new__`以`upperattr_metaclass`为第一参数,并且在`upperattr_metaclass`中被定义。就像你把self作为实例的第一个参数那样,或者作为类方法的第一个参数。
当然,我在这里为了清晰起见,使用了一个很长的名称,但就像self一样,所有的参数都有习惯的名称。所以,在开发实践中所写的元类看起来是这样的:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type.__new__(cls, clsname, bases, uppercase_attr)
我们可以用super使它更清楚,这样将缓解继承(因为,是的,你可以拥有元类,继承metaclasses,继承type):
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)
就是这样。关于元类真的是没有更多要讲的了。
使用元类的代码复杂的原因并不在于元类,那是因为你通常用元类来实现一些奇怪的功能,而这些功能要依靠自省、继承、变量,如:`__dict__`等。
的确,元类对于“魔法”特别有用,这些事务是复杂的,但元类本身很简单:
- 拦截类的创建
- 修改类
- 返回修改后的类
###你为什么要使用元类而不是函数?
`__metaclass__`可以被任意调用。既然类明显更复杂,你为什么还要使用它呢?
这样做有几个理由:
- 目的明确。当你读`UpperAttrMetaclass(type)`时,你知道要遵循的是什么。
- 可以使用面向对象编程。元类可以继承元类、重写父类的方法。元类甚至可以使用元类。
- 可以优化代码。你从不为像上面例子中琐碎的东西而使用元类。它通常用于复杂的东西。在一个类中有多种方法并且将它们优化组合,是非常有价值的,使得代码可读性更强。
- 你可能喜欢使用`__new__`,`__init__`和`__call__`。它们能帮你实现不同的功能。尽管你通常可以用`__new__`来实现所有的功能,有些人还是更喜欢使用`__init__`。
- 这些被称为元类。可恶!它肯定意味着什么!
你为什么会使用元类?
现在的大问题是:为什么你会使用一些复杂难懂、容易出错的特性?
嗯,通常你不会这样做:
>元类是更深层次的魔法,超过99%的用户不需要担心。不要怀疑你是否需要元类(那些真正需要元类的人对此确定无疑,并且不需要解释为什么)。
>——Python专家蒂姆﹒彼得斯
元类的主要使用案例是创建API。一个典型的例子就是Django ORM。
它允许你定义类似这样的东西:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
但如果你这样做:
guy = Person(name='bob', age='35')
print(guy.age)
它不会返回到一个`IntegerField`对象。它会返回到一个int,甚至可以直接从数据库读取。
这是可以实现的,因为`models.Model`定义了`__metaclass__`。它使用魔法把你刚才用简单语句定义的Person转化成一个复杂的钩子连接到数据库字段。
通过显示一个简单的API和元类,Django使复杂的东西看起来简单,再从API重构代码去完成真正的幕后工作。
###最后的话
首先,你知道,类是可以创建实例的对象。
事实上,类本身就是实例。在元类中
>>> class Foo(object): pass
>>> id(Foo)
142630324
在Python中,一切都是对象,它们都是类的实例或元类的实例。
但是type除外。
type实际上是自己的元类。你无法在纯粹的Python中复制它,所以只能在实施层面做点小把戏。
其次,元类是复杂的。你可能不想把它们用于非常简单的类。你可以用两种不同的技术来改变类:
- [猴子补丁monkey patching](http://en.wikipedia.org/wiki/Monkey_patch)
- 类装饰器
当你需要改变类的时候,99%的情况下,使用它们是明智之举。
但99%的时间,你根本不需要改变类。
-------
打赏帐号:qiwsir@126.com(支付宝),qiwsir(微信号)
================================================
FILE: 303.md
================================================
#怎样在Python中创建函数装饰器链?
原问题地址:http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python
##问题:
我怎样在Python中创建能实现以下功能的两个装饰器?
@makebold
@makeitalic
def say():
return "Hello"
返回的是:
<b><i>Hello</i></b>
我不想在实际的应用程序中以这种方式来创建HTML,只是想了解装饰器和装饰器链是怎么回事。
##答案 1:
查看[参考文献](https://docs.python.org/2/reference/compound_stmts.html#function)来了解装饰器的应用。这是你所要找的内容:
def makebold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeitalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makebold
@makeitalic
def hello():
return "hello world"
print hello() ## returns "<b><i>hello world</i></b>"
##答案 2:
###装饰器基础知识
**Python中的函数就是对象**
要了解装饰器,你首先必须深知Python中的函数就是对象。这是重要的。让我们通过一个简单的例子来看看为什么:
def shout(word="yes"):
return word.capitalize()+"!"
print shout()
# outputs : 'Yes!'
# 作为对象,你可以把这个函数赋值给一个变量,像任何其他对象一样
scream = shout
#注意:我们不使用圆括号:不调用函数,我们是把函数“shout”赋给变量“scream”。这意味着你可以从“scream”中调用“shout”:
print scream()
# outputs : 'Yes!'
# 不仅如此,这意味着你可以删除旧的名字'shout',并且仍然可以用'scream'调用这个函数。
del shout
try:
print shout()
except NameError, e:
print e
# outputs: "name 'shout' is not defined"
print scream()
# outputs: 'Yes!'
好的,记住上述内容。我们很快就要用到它。
Python函数的另一个有趣的特性是它们可以在另一个函数里被定义!
def talk():
# 你可以在"talk"中定义一个动态函数…
def whisper(word="yes"):
return word.lower()+"..."
# 然后马上就能使用它!
print whisper()
# 如果你每次调用"talk"的时候,"talk"都被定义为"whisper",那么"whisper"就在"talk"中被调用。
talk()
# outputs:
# "yes..."
# 但"whisper"不存在于"talk"之外:
try:
print whisper()
except NameError, e:
print e
# outputs : "name 'whisper' is not defined"*
# Python的函数是对象
**函数引用**
好吧,还在这儿?现在有趣的部分是…
你已经看到了函数就是对象。因此,函数:
- 可以被赋值给一个变量
- 可以在另一个函数中定义
这意味着一个函数可以返回另一个函数。看一看!☺
def getTalk(kind="shout"):
# 定义动态函数
def shout(word="yes"):
return word.capitalize()+"!"
def whisper(word="yes") :
return word.lower()+"...";
# 然后我们返回到其中一个
if kind == "shout":
# 不使用"()",我们不是在调用函数,
# 是在返回函数对象
return shout
else:
return whisper
# 你怎么使用这个奇怪的工具?
# 获取函数并将它赋给变量
talk = getTalk()
# 你可以看到这里的"talk"是一个函数对象:
print talk
# outputs : <function shout at 0xb7ea817c>
# 此函数所返回的就是这个对象:
print talk()
#outputs : Yes!
# 如果你感到亢奋,你甚至可以直接使用它:
print getTalk("whisper")()
#outputs : yes...
但是,等等…还有更多!
如果你可以返回一个函数,你就可以把一个函数当作参数:
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!
好的,你已经看到了理解装饰器所需要的所有知识。你看,装饰器是“包装器”。这意味着装饰器可以使你在它所装饰的函数之前和之后运行代码,而无需修改函数本身。
**装饰器的手动操作**
这样来进行装饰器的手动操做:
# 装饰器是一个函数,它把另一个函数作为参数
def my_shiny_new_decorator(a_function_to_decorate):
# 函数里面,装饰器定义了一个动态函数:the wrapper。
# 这个函数包含原函数,因此它可以在原函数之前和之后运行代码。
def the_wrapper_around_the_original_function():
# 把你希望在原函数被调用之前就执行的代码放在这里
print "Before the function runs"
# 调用函数(使用括号)
a_function_to_decorate()
# 把你希望在原函数被调用之后才执行的代码放在这里
print "After the function runs"
# 这时候,"a_function_to_decorate"从未被执行。
# 回到我们刚才创建的包装器函数。
# 包装器中包含在原函数被执行之前和之后的函数和代码。它是现成的!
return the_wrapper_around_the_original_function
#现在想象你创建一个你不想再改动的函数。
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
#你可以装饰它,以便扩展它的功能。
#只需要把它传递给装饰器,装饰器就会把它动态地装进任何你想要的代码中,
#并且为你返回一个新的可用的函数:
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_function`的时候,实际上就是在调用`a_stand_alone_function_decorated`。这很容易,你只需要借助`my_shiny_new_decorator`返回的函数覆盖`a_stand_alone_function`:
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
# 你猜会怎么样?这正是装饰器所实现的功能!
**装饰器揭秘**
前面的例子中使用了装饰器句法:
@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)
装饰器是装饰器设计模式[decorator design pattern](http://en.wikipedia.org/wiki/Decorator_pattern)的Python语言的变体。在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~
现在:回答这个问题…
作为结论,你可以很容易地领会到如何回答这个问题:
# 使字体变粗的装饰器
def makebold(fn):
# 装饰器所返回的新函数
def wrapper():
# 在函数之前和之后插入某些代码
return "<b>" + fn() + "</b>"
return wrapper
# 斜体字的装饰器
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>
你现在可以快乐地离开,或者多花点心思看一看装饰器的高级应用。
###装饰器应用的更高等级
**将参数传递给被装饰的函数**
# 这不是魔法,你只需要让包装器传递参数:
def a_decorator_passing_arguments(function_to_decorate):
def a_wrapper_accepting_arguments(arg1, arg2):
print "I got args! Look:", arg1, arg2
function_to_decorate(arg1, arg2)
return a_wrapper_accepting_arguments
# 因为当你调用被装饰器返回的函数时,你就是在调用包装器,把参数传递给包装器会使包装器把函数传递给被装饰的函数
@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
print "My name is", 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 %s, what did you think?" % (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):
# 包装器接受任何参数
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 %s, %s and %s like platypus? %s" %\
(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 %s, what did you think ?" % (self.age + lie)
m = Mary()
m.sayYourAge()
# outputs
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?
**把参数传递给装饰器**
好,现在你对于把参数传递给装饰器本身有什么看法?
这可能有点怪异,因为装饰器必须接受一个函数作为参数。因此,你不能把被装饰的函数的参数直接传递给装饰器。
在急着去解决问题之前,让我们写一个小小的提示:
# 装饰器是普通函数
def my_decorator(func):
print "I am an ordinary function"
def wrapper():
print "I am function returned by the decorator"
func()
return wrapper
# 因此,即使没有任何“@”,你也可以调用它
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`的函数。
这是很重要的!你所给出的标签可以直接指向装饰器,或者相反。
让我们搞个小把戏。
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
# 让我们创建一个装饰器。它只是一个新函数而已。
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
# 然后我们对这个函数进行装饰
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
# 我们来调用这个函数:
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.
#最后:
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.
decorated_function()
嘿,你看到了吗?我们在调用函数时使用了 `@`语句。
所以,返回到带有函数的装饰器。如果我们可以使用函数来生成动态的装饰器,我们就可以将参数传递给函数,对吗?
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2
def my_decorator(func):
# 这里的传递参数的功能是得益于闭包。
# 如果你不喜欢使用闭包,你可以假定它是ok或read:http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2
# 不要混淆装饰器参数和函数参数!
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 knows about my arguments: Leslie Howard
正如你可以看到的那样,你可以通过这个小把戏,把参数传递给和任何函数一样的装饰器。如果你愿意,你甚至可以使用*args 和**kwargs。但是记住,装饰器只能被调用一次,也就是当Python导入脚本的时候。在这之后,你就不能动态地设置参数。当你使用“import x”的时候,参数已经被装饰,所以你无法改变什么。
让我们实践一下:修饰装饰器
好吧,作为奖励,我会给你提供一段练习,使得任何一个装饰器接受通用的任何参数。毕竟,为了接受参数,我们用另一个函数创建了我们的装饰器。
我们包装了装饰器。
我们最近有没有看到任何其它的被包装函数?
哦,是的,装饰器!
让我们找一些乐趣,包装一下装饰器:
def decorator_with_args(decorator_to_enhance):
"""
这个函数应该被用作装饰器。
它必须装饰另一个被确定用作装饰器的函数。
喝一杯咖啡。
这将允许任何装饰器接受任意数量的参数,
这样你就不必每一次都要记着如何去做。
"""
#我们用相同的把戏来传递参数
def decorator_maker(*args, **kwargs):
# 我们动态地创建只接受一个函数的装饰器,但对外隐藏所传递的参数。
def decorator_wrapper(func):
# 我们返回到原始装饰器的结果,它毕竟只是一个普通的函数(返回到一个函数)。
# 唯一的陷阱:装饰器必须有这个独特的签名,否则就无法运行:
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker
它的使用方法如下:
# 创建被用作装饰器的函数,并在上面贴上装饰器的标签:-)
# 不要忘记“decorator(func, *args, **kwargs)”
@decorator_with_args
def decorated_decorator(func, *args, **kwargs):
def wrapper(function_arg1, function_arg2):
print "Decorated with", args, kwargs
return func(function_arg1, function_arg2)
return wrapper
# 然后你可以根据自己的意愿,用全新的装饰器来装饰函数。
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
print "Hello", function_arg1, function_arg2
decorated_function("Universe and", "everything")
#outputs:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything
# Whoooot!
如果你不喜欢过长的解释,参见Paolo Bergantino’s的回答(即答案1)。
我知道,你最后一次有这种感觉是在听一个人说“在理解递归之前,你必须先认识递归”的时候。但现在你掌握了装饰器,是不是感觉良好呢?
**最佳实践:装饰器**
Python 2.4增加了装饰器,所以要确保你的代码在大于或等于 2.4的Python版本上运行。
装饰器会减缓对函数的调用。记住这一点。
你无法撤销对函数的装饰。(有些技巧能用于创建可以撤销的装饰器,但是无人采用。)所以一旦函数被装饰,它所有的代码就都被装饰。
装饰器对函数进行包装,这使函数很难调试。(在大于或等于2.5的Python版本中有所改进;见下文。)
Python 2.5开始有了functools模块。它包括函数functools.wraps()。这个函数把名称、模块和被装饰函数的函数文档字符串拷贝到包装器中。
(有趣的事实:functools.wraps()是装饰器!☺)
# 为了便于调试,堆栈轨迹为你打印出函数`__name__`
def foo():
print "foo"
print foo.__name__
#outputs: foo
# 有了装饰器的它变得有些凌乱
def bar(func):
def wrapper():
print "bar"
return func()
return wrapper
@bar
def foo():
print "foo"
print foo.__name__
#outputs: wrapper
#"functools"对此有所帮助,
import functools
def bar(func):
#我们所说的"wrapper"正在包装"func",魔力开始出现了。
@functools.wraps(func)
def wrapper():
print "bar"
return func()
return wrapper
@bar
def foo():
print "foo"
print foo.__name__
#outputs: foo
如何让装饰器派上用场?
现在的大问题是:我可以用装饰器做什么呢?
装饰器似乎很酷很强大,但有一个实际的例子还是好的。装饰器的使用可以有1000种可能性。经典的用途是从外部静态数据连接库对函数的功能进行扩展(你不能修改它),或者用于调试(你无需修改它,因为它是暂时的)。
你可以遵循DRY (Don't Repeat Yourself)原则用装饰器来扩展几个函数,比如:
def benchmark(func):
"""
一个记录函数执行时间的装饰器。
"""
import time
def wrapper(*args, **kwargs):
t = time.clock()
res = func(*args, **kwargs)
print func.__name__, time.clock()-t
return res
return wrapper
def logging(func):
"""
一个记录脚本活动的装饰器。(它实际上只是打印它,但它可以记录!)
"""
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
print func.__name__, args, kwargs
return res
return wrapper
def counter(func):
"""
一个统计和打印函数运行次数的装饰器
"""
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
当然,装修器的好处是,你几乎可以在任何内容上立即使用它们,而无需重写。我说过DRY(Don't Repeat Yourself原则,写代码的时候尽量避免重复的实现)。
@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本身提供了好几种装饰器:property、staticmethod等。
- Django使用装饰器来管理高速缓存和视图的权限。
- Twisted来假冒内联异步函数的调用。
这真是一个大型游乐场。
================================================
FILE: 304.md
================================================
#`if __name__ == "__main__"`是做什么用的?
原问题地址:http://stackoverflow.com/questions/419163/what-does-if-name-main-do
##问题:
`if __name__ == "__main__"`是做什么用的?
# Threading example
import time, thread
def myfunction(string, sleeptime, lock, *args):
while 1:
lock.acquire()
time.sleep(sleeptime)
lock.release()
time.sleep(sleeptime)
if __name__ == "__main__":
lock = thread.allocate_lock()
thread.start_new_thread(myfunction, ("Thread #: 1", 2, lock))
thread.start_new_thread(myfunction, ("Thread #: 2", 2, lock))
##答案:
当Python解释器读取源文件时,它执行在Python中所能找到的所有代码。在执行代码之前,它将定义一些特殊变量。例如,如果Python解释器把这个模块(源文件)作为主程序来运行,它就把特殊变量`__name__`的值设置为`__main__`。如果这个文件从另一个模块被引入,`__name__`将被设置为模块的名字。
至于你的脚本,让我们假设它正在执行主函数,例如,你在命令行键入下面的内容
python threading_example.py
设置了特定的变量后,Python将执行`import`语句并加载这些模块。然后,执行`def`语句块,创建一个函数对象并实现`myfunction`这个名称指向函数对象。接着,它会读取if语句,并确保`__name__`等于`__main__`,之后,它将执行所显示的语句块。
这样做的原因之一是:有时候你把一个模块(.py文件)写在可以直接被执行的地方。另外,这个模块也可以被引入,并在另一个模块中被使用。经过`__main__`检查,你可以让代码只有在你想把该模块作为程序来运行的时候才被执行,而不是在有人只想引用模块并调用你的函数时被执行。
你可以访问这个页面来了解一些额外的细节,[this page](http://ibiblio.org/g2swap/byteofpython/read/module-name.html)
-------
打赏帐号:qiwsir@126.com(支付宝)
================================================
FILE: 305.md
================================================
#@staticmethod(静态方法)和@classmethod(类方法)有什么区别?
原问题地址:http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python
##问题:
用[@staticmethod](http://docs.python.org/2/library/functions.html#staticmethod)装饰的函数和用[ @classmethod](http://docs.python.org/2/library/functions.html#classmethod)装饰的函数有什么区别?
##答案:
也许一些代码示例会有所帮助。注意`foo`、 `class_foo` 和 `static_foo` 在调用方面的差异:
class A(object):
def foo(self,x):
print "executing foo(%s,%s)"%(self,x)
@classmethod
def class_foo(cls,x):
print "executing class_foo(%s,%s)"%(cls,x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)"%x
a=A()
下面是在实例中来调用方法的一种常用方式。实例`a`,被当作第一个参数隐式传递。
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
装饰类方法(classmethods),实例的类是作为第一个参数隐式传递,从而代替了`self`。
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
你还可以使用类来调用`class_foo`。事实上,如果你把某个东西定义为classmethod,可能是因为你打算从类而不是从类的实例中调用它。`A.foo(1)`可能会引发TypeError,但`A.class_foo(1)`刚刚好:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
人们已经发现类方法的一种用途是[创建可继承的替代性构造函数(inheritable alternative constructors)](http://stackoverflow.com/a/1950927/190597)。
在静态方法中,self(对象实例)和cls(类)都不能作为第一个参数来隐式传递。它们的功能就像普通函数一样,只不过你可以从实例或类来调用它们:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Staticmethods用来对函数进行分组,这些函数和某个类有着逻辑关联。
`foo`只是一个函数,但是当你调用`a.foo`时,你得到的不只是函数本身,你还会得到该函数的一个“局部应用”的版本,并且对象实例被绑定为该函数的第一个参数。`foo`有2个参数,而`a.foo`只有1个参数。
下面这个术语“绑定”的意思是说:`a`被绑定到`foo`。
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
在`a.class_foo`中,被绑定到`class_foo`的不是`a`,而是`A`这个类。
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
尽管这里的staticmethod是一个方法,a.static_foo也只是返回函数,并且这个函数没有绑定的参数。static_foo 有1个参数,a.static_foo也有1个参数。
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
当然,当你用A这个类来调用static_foo的时候,会出现同样的情况。
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
【编者注:另外对静态方法和类方法的比较,可以参考:[《零基础学Python(第二版)之类(5)部分》](https://github.com/qiwsir/StarterLearningPython/blob/master/210.md)】
================================================
FILE: 306.md
================================================
#如何通过参数来传递一个变量?
原问题地址:http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference
##问题:
Python文档似乎没有明确指出参数是否通过引用或通过值进行传递,而且下面的代码产生了不变的值“Original”
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.Change(self.variable)
print self.variable
def Change(self, var):
var = 'Changed'
我怎样通过实际的参数来传递变量?
##答案:
参数是[通过引用来传递的(passed by assignment)](http://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference)。其原理是双重的:
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])
虽然这样做似乎有点麻烦。
================================================
FILE: 307.md
================================================
#Python中的 `__str__`和`__repr__`之间的区别
原问题地址:http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python
##问题:
Python中的`__str__`和`__repr__`有什么区别呢?
##回答:
Alex总结得很好,但是简洁得出乎意料。
首先,让我重申Alex的主要观点:
- 默认实现是没有用的(很难想象一个有用的默认实现,但事实的确如此)
- `__repr__`目标是准确性
- `__str__`目标是可读性
- 容器的`__str__`使用容器所包含对象的`__repr__`
###默认实现是没有用的
多半是惊喜,因为Python的默认值往往是相当有用的。然而,在这种情况下,有一个像这样的针对`__repr__`的默认实现
return "%s(%r)" % (self.__class__, self.__dict__)
会有很大的危险(例如,如果对象之间相互引用,太容易陷入无限递归)。所以Python避开了这一点。注意,有一个默认实现:如果`__repr__`被定义,并且`__str__`没有被定义,对象的工作原理就好像`__str__=__repr__`。
简单来说,这意味着:几乎你所实现的每一个对象都应该有一个`__repr__`函数来用于理解这个对象。实现`__str__`是可选的:如果你需要 “优质打印”功能(例如,用于报表)【译者注:“优质打印”,意思是要得到非常友好的输出效果】,你就要实现`__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)
但是你必须做到最后一步——确保你所实现的每个对象都有一个可用的repr函数,那样的代码才可以正常运行。下面解释为什么要使用“eval”:如果你有足够的信息,能实现 `eval(repr(c))==c`,这就意味着你所掌握的信息足以让你了解c。如果这样做很容易,做下去,至少用一个模糊的方式来做。如果不容易,无论如何也要确保你获取了关于c的足够的信息。我通常使用类似于eval的格式:`"MyClass(this=%r,that=%r)" % (self.this,self.that)`。这并不意味着你可以真正构建MyClass或者那些正确的构造函数参数——但它对于表达“这是你需要了解的关于这个实例所有信息”是一种有用的形式。
注意:我在上面使用的是%r,而不是%s。在`__repr__`实现中,你总是用`repr()` [或相同的格式化字符%r],否则无法达成repr的目标。你要能够区分`MyClass(3)`和`MyClass("3")`。
###`__str__`的目标在于可读性
特别说明,它不在意准确性——注意`str(3)==str("3")`。同样地,如果你要实现IP地址抽象,有个类似于192.168.1.1的str就好。当实现一个日期/时间抽象时,str可以是“2010/4/12 15:35:22”等。目标在于:让用户而不是程序员更容易阅读。删掉无用的数字,伪装成其它的类——只要它具有可读性,这就是一种改进。
###容器的`__str__`使用了所包含对象的`__repr__`
这似乎令人惊讶,不是吗?这的确有点意外,但可读性如何?
[moshe is, 3, hello world, this is a list, oh I don't know, containing just 4 elements]
可读性不很强。具体而言,容器中字符串的表示形式太容易受干扰。面对歧义,记住,Python拒绝猜测。如果你在打印一个列表的时候你想要实现上述功能,只需要
print "["+", ".join(l)+"]"
(或许你也可以弄清楚字典的相关方法。)
###总结
对于你所实现的任何的类都可以使用`__repr__`。这应该成为你的习惯。如果你认为实现`__str__`导致了歧义,但较少有错误,并有更好的可读性,则可以使用`__str__`。
================================================
FILE: 308.md
================================================
#如何使用 ' super '
原问题地址:http://stackoverflow.com/questions/222877/how-to-use-super-in-python
##问题:
有人能给我解释下面两组代码的区别吗?
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
和这个:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
我在类里面看到过很多关于super的应用,这些类都只有单一继承。我能明白为什么你会在多重继承中使用它,但我不清楚在这种情况下使用它有什么优势。
##回答 1:
在单一继承中使用`super()`的好处微乎其微。主要的好处是:你不需要把基类的名称硬编码到每一个使用父类方法的方法中。
然而,使用不带有`super()`的多重继承几乎是不可能的。包括常见的如混入类【译者注:关于mixin(混入类),推荐阅读:[设计模式 Mixin (混入类)](http://www.cnblogs.com/youxin/p/3623329.html)】、接口、抽象类,等等。这样的拓展了你编码。如果有人将来想创建一个类,让这个类从子类扩展到混入类,他们的代码将无法正常运行。
##回答 2:
###间接的向前兼容性
super的应用给你带来了什么?对于单一继承,上述内容实际上是从静态分析的角度来看问题的。然而,super使你的代码具有间接的向前兼容性。
向前兼容性对经验丰富的开发人员非常重要。你希望在你对代码做出修改后,能够以最低限度的代价继续工作。当你查看修订记录时,你想精确地看到修改的时间和内容。
你可以从单一继承开始,但是如果你决定添加另一个基类,你只需要改变基类那行。特别是在Python 2中,恰当地获取super的参数和正确的方法参数是很困难的。如果你知道你在正确使用带有单一继承的super,调试的进展就不那么困难。
###依赖注入
其他人可以使用你的代码,并向你的方法解析中注入父类方法:
class SomeBaseClass(object):
def __init__(self):
print('SomeBaseClass.__init__(self) called')
class Child(SomeBaseClass):
def __init__(self):
print('Child.__init__(self) called')
SomeBaseClass.__init__(self)
class SuperChild(SomeBaseClass):
def __init__(self):
print('SuperChild.__init__(self) called')
super(SuperChild, self).__init__()
例如,你把另一个类添加到你的对象,并且想要在Foo和Bar之间注入类(用于测试或其他原因):
class InjectMe(SomeBaseClass):
def __init__(self):
print('InjectMe.__init__(self) called')
super(InjectMe, self).__init__()
class UnsuperInjector(Child, InjectMe): pass
class SuperInjector(SuperChild, InjectMe): pass
使用不带有super的子类没能成功地注入依赖,因为你试图通过硬编码方法调用正在使用的子类本身:
>>> o = UnsuperInjector()
Child.__init__(self) called
SomeBaseClass.__init__(self) called
然而,如果你的类所包含的子类使用了super,这个类就可以正确地注入依赖:
>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
不使用super有可能给你的代码用户带来不必要的限制。
================================================
FILE: 309.md
================================================
#Python中的静态方法?
原问题地址:http://stackoverflow.com/questions/735975/static-methods-in-python
##问题:
在Python中有没有可能调用静态方法而无需对类进行初始化,如:
ClassName.StaticMethod ( )
##回答:
是的,使用staticmethod装饰器
class MyClass(object):
@staticmethod
def the_static_method(x):
print x
MyClass.the_static_method(2) # outputs 2
请注意,某些代码可能使用老方法来定义静态法,把静态法作为函数而不是装饰器来应用。如果你不得不支持Python的老版本(2.2和2.3)的时候,才会这样用。
class MyClass(object):
def the_static_method(x):
print x
the_static_method = staticmethod(the_static_method)
MyClass.the_static_method(2) # outputs 2
这和第一个例子完全相同(使用`@staticmethod`),只是没有采用好用的装饰器语法。
最后,有节制地使用`staticmethod()`!在Python中只有极少数情况下才有必要使用静态方法。我见过对于`staticmethod()`的多次使用,而这些时候,独特的“顶级”函数往往表达得更清晰。
================================================
FILE: 401.md
================================================
#在Python中调用外部命令
原问题地址:http://stackoverflow.com/questions/89228/calling-an-external-command-in-python
##问题:
我如何在Python脚本内部调用外部命令(就好像我在Unix shell命令行或者在Windows命令提示符中键入外部命令那样)?
##答案 1:
参看标准函数库中的[subprocess module](https://docs.python.org/2/library/subprocess.html):
from subprocess import call
call(["ls", "-l"])
Subprocess模块相对于`os.system`的优势在于它的灵活性更强(你可以获得标准输出、标准错误、“真实”的状态码、更好的错误处理,等等)。我认为os.system已过时或即将过时(参考阅读:[https://docs.python.org/2/library/subprocess.html#replacing-older-functions-with-the-subprocess-module](https://docs.python.org/2/library/subprocess.html#replacing-older-functions-with-the-subprocess-module))
然而,os.system对于快速上手的脚本或一次性脚本是够用的。
##答案 2:
这里总结了调用外部程序的方法和每个方法的优缺点:
1、`os.system("some_command with args")`把命令和参数发送到shell。这是很好的,因为这种方式实际上可以使你同时运行多个命令、设置管道和输入/输出重定向。例如,
os.system("some_command < input_file | another_command > output_file")
然而,虽然这样做很方便,你却必须处理转义shell中的字符,如:空格等。另一方面,这也使得你所运行的命令仅仅是shell命令,实际上并不是外部程序。
[阅读材料](https://docs.python.org/2/library/os.html#os.system)
2、`stream = os.popen("some_command with args")`也可以实现`os.system`的功能,只不过它给了你一个类似文件的对象,有了这个对象,你就可以为那个进程使用标准输入/输出。对于os.popen还有3种其他的变式,它们在处理i/o的方式上略有不同。如果你把每个命令都当作字符串来传递,那么你的命令就被传递到shell;如果你把这些命令作为列表来传递,你就不必担心转义的问题。[阅读材料](https://docs.python.org/2/library/os.html#os.popen)
3、`subprocess`模块的`Popen`类。`Popen`旨在替代`os.popen`,但它本身的综合性使它有点复杂。例如,你会使用
print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
而不是
print os.popen("echo Hello World").read()
这些选择都是在一个统一的类中而不是在4个不同的popen函数中,这总归是好的。[阅读材料](https://docs.python.org/2/library/subprocess.html#popen-constructor)
4、从subprocess模块中调用函数。这基本上就像Popen类一样,采用的都是相同的参数,但它只是等待命令完成并返回代码。例如:
return_code = subprocess.call("echo Hello World", shell=True)
[阅读材料](https://docs.python.org/2/library/subprocess.html#subprocess.call)
5、如果你使用的是Python 3.5或更高版本,你可以使用新的[subprocess.run功能](https://docs.python.org/3.5/library/subprocess.html#subprocess.run)。`subprocess.run`和以上的方法很像,但更灵活,而且当命令执行完毕时,返回[CompletedProcess对象](https://docs.python.org/3.5/library/subprocess.html#subprocess.CompletedProcess)。
6、os模块具有C程序中的所有的`fork/exec/spawn`功能,但我不建议直接使用它们。
也许你应该使用的是subprocess模块。
最后,请注意:在所有的方法中,你把最终命令作为一个字符串发送到shell来执行的时候,你就要负责对它进行转义。如果你所发送的任何部分的字符串不能完全被信任,就会有严重的安全隐患。(例如,如果用户正在确认执行某一部分或任何部分的字指令)。如果你不确定,只使用这些带有常量的方法。为了对这些隐患稍作了解,考虑一下这个代码
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
想象一下,用户执行“my mama didnt love me && rm -rf /”。
-------
打赏帐号:qiwsir@126.com(支付宝),qiwsir(微信号)
================================================
FILE: 402.md
================================================
#在Python中检查目录是否存在,如果有必要就创建一个目录
原问题地址:http://stackoverflow.com/questions/273192/in-python-check-if-a-directory-exists-and-create-it-if-necessary
##问题:
把文件写入某个目录之前,想要检查这个目录是否存在,实现这个目的的最优雅的方式是什么?如果这个目录不存在,用Python创建此目录的最优雅的方式是什么?这是我的尝试:
filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)
try:
os.stat(dir)
except:
os.mkdir(dir)
f = file(filename)
不知怎的,我漏掉了`os.path.exists`(感谢kanja, Blair, and Douglas)。这是我现在得出的代码:
def ensure_dir(f):
d = os.path.dirname(f)
if not os.path.exists(d):
os.makedirs(d)
是否有一个“open”指令,使得这项功能自动执行?
##答案:
我看到了两个回答,它们各有可取之处,也各有一个小的缺陷,所以我给出我的回答:
尝试`os.path.exists`,并考虑把`os.makedirs`用于创建目录。
if not os.path.exists(directory):
os.makedirs(directory)
正如在注解和其他地方所指出的那样,这里有一个竞态条件:如果目录创建于调用`os.path.exists`和`os.makedirs`之时,`os.makedirs`将以OSError的形式宣告失败。不幸的是,重写OSError和继续执行皆非万无一失。由于权限不足、磁盘已满等其他因素,`os.makedirs`将忽略某项失败而创建该目录。
有一种选择是阻止OSError并检查错误,如果你熟悉情况(在我的操作系统中,13似乎表明权限被拒绝,17表明文件存在。目前尚不清楚,这是否更适合远程便携式操作,但[Is there a cross-platform way of getting information from Python’s OSError](http://stackoverflow.com/questions/273698/is-there-a-cross-platform-way-of-getting-information-from-pythons-oserror))对此进行了探究。或者,可能还有第二个`os.path.exists`,但假设在初次检测后由另一个途经创建了目录,然后把目录移到二次检测之前,我们仍然有可能被愚弄。
根据应用程序的不同,并行操作的危险可能比其他因素所构成的危险更多或更少,例如文件权限。在选择实施前,开发人员必须对特定的应用程序和其预期的环境有更多的了解。
================================================
FILE: README.md
================================================
如果你是一名自诩为程序员的,那么,就必须知道stackoverflow.com这个网站。因为它是一个非常好的技术问答网站,里面不仅有高手,更重要的是有对问题进行详细耐心解答的高手。
这里我抽空翻译了一些典型的问答,当然,只能翻译Python语言方面的,其它语言,唯恐因知之甚少而漏洞百出,即便是Python,也会因为才疏学浅,在翻译过程中感到惶恐。所以,读者如果看到错误之处,请不吝赐教。
欢迎访问:[www.itdiffer.com](http://www.itdiffer.com),加入学习Python的群组。
#目录
##基本对象和语句
- [Python是否有三元条件运算符](./101.md)
- [如何把两个Python字典用一个表达式合并](./102.md)
- [对Python字典按值排序](./103.md)
- [append和extend的区别](./104.md)
- [获得Python的循环索引](./105.md)
- [判断一个字符串是否含子串的方法](./106.md)
- [怎样在Python中实现Enum(枚举)的功能](./107.md)
- [将多行的异常变成一行](./108.md)
- [解释Python的切片法](./109.md)
- [如何在Python中获取当前时间](./110.md)
- [查看某个给定的键是否已经在字典中](./111.md)
- [如何删除Python中的换行符?](./112.md)
- [如何将Python列表均匀划分?](./113.md)
- [join函数为什么是string.join(list)而不是list.join(string)](./114.md)
- [我如何在Python中检查一个字符串否可转化为数字?](./115.md)
##文件
- [如何使用Python来检查文件是否存在](./201.md)
- [如何在Python中列出目录下的所有文件](./202.md)
##函数和类
- [Python中yield关键词的作用是什么](./301.md)
- [Python中的元类是什么](./302.md)
- [怎样在Python中创建函数装饰器链](./303.md)
- [`if __name__ == "__main__"`是做什么用的](./304.md)
- [@staticmethod(静态方法)和@classmethod(类方法)的区别](./305.md)
- [如何通过参数来传递一个变量?](./306.md)
- [Python中的 `__str__`和`__repr__`之间的区别](./307.md)
- [如何使用 ' super '](./308.md)
- [Python中的静态方法?](./309.md)
##系统
- [在Python中调用外部命令](./401.md)
- [检查目录是否存在,如果有必要就创建一个目录](./402.md)
##网络
gitextract_jkhcmjtr/ ├── 101.md ├── 102.md ├── 103.md ├── 104.md ├── 105.md ├── 106.md ├── 107.md ├── 108.md ├── 109.md ├── 110.md ├── 111.md ├── 112.md ├── 113.md ├── 114.md ├── 115.md ├── 201.md ├── 202.md ├── 301.md ├── 302.md ├── 303.md ├── 304.md ├── 305.md ├── 306.md ├── 307.md ├── 308.md ├── 309.md ├── 401.md ├── 402.md └── README.md
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (127K chars).
[
{
"path": "101.md",
"chars": 878,
"preview": "#Python是否有三元条件运算符?\n\n原问题地址:http://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator\n\n##问"
},
{
"path": "102.md",
"chars": 5353,
"preview": "#如何把两个Python字典用一个表达式合并?\n\n原问题地址:http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-in-a-sin"
},
{
"path": "103.md",
"chars": 1086,
"preview": "#对Python字典按值排序\n\n原问题地址:http://stackoverflow.com/questions/613183/sort-a-python-dictionary-by-value\n\n##问题:\n\n我有一个值的字典,它的值来自"
},
{
"path": "104.md",
"chars": 474,
"preview": "#append和extend的区别\n\n原问题地址:http://stackoverflow.com/questions/252703/python-append-vs-extend\n\n##问题\n\n列表的两个方法`append()`和`ext"
},
{
"path": "105.md",
"chars": 490,
"preview": "#获得Python的循环索引\n\n原问题地址:http://stackoverflow.com/questions/522563/accessing-the-index-in-python-for-loops\n\n##问题:\n\n是否有人知道如何"
},
{
"path": "106.md",
"chars": 726,
"preview": "#判断一个字符串是否含子串的方法\n\n原问题地址:http://stackoverflow.com/questions/3437059/does-python-have-a-string-contains-substring-method\n\n"
},
{
"path": "107.md",
"chars": 1754,
"preview": "#怎样在Python中实现Enum(枚举)的功能\n\n原问题地址:http://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python\n\n##问题:\n\n我"
},
{
"path": "108.md",
"chars": 1140,
"preview": "#将多行的异常变成一行\n\n原问题地址:http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block\n\n##问题:\n"
},
{
"path": "109.md",
"chars": 919,
"preview": "#解释Python的切片法\n\n原问题地址:\nhttp://stackoverflow.com/questions/509211/explain-pythons-slice-notation\n\n##问题:\n\n我需要关于Python切片明确说明"
},
{
"path": "110.md",
"chars": 546,
"preview": "#如何在Python中获取当前时间\n\n原问题地址:http://stackoverflow.com/questions/415511/how-to-get-current-time-in-python\n\n##问题:\n\n获取当前时间所用的模块"
},
{
"path": "111.md",
"chars": 807,
"preview": "#查看某个给定的键是否已经在字典中\n\n原问题地址:http://stackoverflow.com/questions/1602934/check-if-a-given-key-already-exists-in-a-dictionary\n"
},
{
"path": "112.md",
"chars": 547,
"preview": "#如何删除Python中的换行符?\n\n原问题地址:\nhttp://stackoverflow.com/questions/275018/how-can-i-remove-chomp-a-newline-in-python\n\n##问题:\n\n在"
},
{
"path": "113.md",
"chars": 1351,
"preview": "#如何将Python列表均匀划分?\n\n原问题地址:http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-p"
},
{
"path": "114.md",
"chars": 592,
"preview": "#Python中的join函数为什么是string.join(list)而不是list.join(string)?\n\n原问题地址:http://stackoverflow.com/questions/493819/python-join-w"
},
{
"path": "115.md",
"chars": 630,
"preview": "#我如何在Python中检查一个字符串否可转化为数字?\n\n原问题地址:http://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-floa"
},
{
"path": "201.md",
"chars": 510,
"preview": "#如何使用Python来检查文件是否存在?\n\n原问题地址:http://stackoverflow.com/questions/82831/how-to-check-whether-a-file-exists-using-python\n\n#"
},
{
"path": "202.md",
"chars": 944,
"preview": "#如何在Python中列出目录下的所有文件\n\n原问题地址:http://stackoverflow.com/questions/3207219/how-to-list-all-files-of-a-directory-in-python\n\n"
},
{
"path": "301.md",
"chars": 8357,
"preview": "#Python中yield关键词的作用是什么?\n\n原问题地址:http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python\n\n##问题:"
},
{
"path": "302.md",
"chars": 14906,
"preview": "#Python中的元类是什么?\n\n原问题地址:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python\n\n##问题:\n\n什么是元类?使用它们能做什么?\n\n"
},
{
"path": "303.md",
"chars": 22688,
"preview": "#怎样在Python中创建函数装饰器链?\n\n原问题地址:http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-p"
},
{
"path": "304.md",
"chars": 1251,
"preview": "#`if __name__ == \"__main__\"`是做什么用的?\n\n原问题地址:http://stackoverflow.com/questions/419163/what-does-if-name-main-do\n\n##问题:\n\n`"
},
{
"path": "305.md",
"chars": 2231,
"preview": "#@staticmethod(静态方法)和@classmethod(类方法)有什么区别?\n\n原问题地址:http://stackoverflow.com/questions/136097/what-is-the-difference-bet"
},
{
"path": "306.md",
"chars": 3703,
"preview": "#如何通过参数来传递一个变量?\n\n原问题地址:http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference\n\n##问题:\n\nPython文档似"
},
{
"path": "307.md",
"chars": 2058,
"preview": "#Python中的 `__str__`和`__repr__`之间的区别\n\n原问题地址:http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in"
},
{
"path": "308.md",
"chars": 2099,
"preview": "#如何使用 ' super '\n\n原问题地址:http://stackoverflow.com/questions/222877/how-to-use-super-in-python\n\n##问题:\n\n有人能给我解释下面两组代码的区别吗?\n\n"
},
{
"path": "309.md",
"chars": 762,
"preview": "#Python中的静态方法?\n\n原问题地址:http://stackoverflow.com/questions/735975/static-methods-in-python\n\n##问题:\n\n在Python中有没有可能调用静态方法而无需对"
},
{
"path": "401.md",
"chars": 2514,
"preview": "#在Python中调用外部命令\n\n原问题地址:http://stackoverflow.com/questions/89228/calling-an-external-command-in-python\n\n##问题:\n\n我如何在Python"
},
{
"path": "402.md",
"chars": 1394,
"preview": "#在Python中检查目录是否存在,如果有必要就创建一个目录\n\n原问题地址:http://stackoverflow.com/questions/273192/in-python-check-if-a-directory-exists-an"
},
{
"path": "README.md",
"chars": 1277,
"preview": "如果你是一名自诩为程序员的,那么,就必须知道stackoverflow.com这个网站。因为它是一个非常好的技术问答网站,里面不仅有高手,更重要的是有对问题进行详细耐心解答的高手。\n\n这里我抽空翻译了一些典型的问答,当然,只能翻译Pytho"
}
]
About this extraction
This page contains the full source code of the qiwsir/StackOverFlowCn GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (80.1 KB), approximately 31.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.