http://satyajit.ranjeev.in/2012/05/17/python-a-few-things-to-remember.html
A few things to remember while coding in Python.
UPDATE: There has been much discussion in Hacker News about this article. A few corrections from it.
-
Zen of Python
Learning the culture that surrounds a language brings you one step closer to being a better programmer. If you haven’t read the Zen of Python yet open a Python prompt and type
import this
. For each of the item on the list you can find examples here http://artifex.org/~hblanks/talks/2011/pep20_by_example.htmlOne caught my attention:
Beautiful is better than ugly
Give me a function that takes a list of numbers and returns only the even ones, divided by two.
#----------------------------------------------------------------------- halve_evens_only = lambda nums: map(lambda i: i/2, filter(lambda i: not i%2, nums)) #----------------------------------------------------------------------- def halve_evens_only(nums): return [i/2 for i in nums if not i % 2]
-
Remember the very simple things in Python
- Swaping two variables:
a, b = b, a
- The step argument in slice operators. For example:
a = [1,2,3,4,5] >>> a[::2] # iterate over the whole list in 2-increments [1,3,5] The special case `x[::-1]` is a useful idiom for 'x reversed'. >>> a[::-1] [5,4,3,2,1]
UPDATE: Do keep in mind
x.reverse()
reverses the list in place and slices gives you the ability to do this:>>> x[::-1] [5, 4, 3, 2, 1] >>> x[::-2] [5, 3, 1]
-
Don’t use mutables as defaults
def function(x, l=[]): # Don't do this def function(x, l=None): # Way better if l is None: l = []
UPDATE: I realise I haven’t explained why. I would recommend reading the article by Fredrik Lundh. In short it is by design that this happens. “Default parameter values are always evaluated when, and only when, the “def” statement they belong to is executed;”
-
Use
iteritems
rather thanitems
iteritems
usesgenerators
and thus are better while iterating through very large lists.d = {1: "1", 2: "2", 3: "3"} for key, val in d.items() # builds complete list when called. for key, val in d.iteritems() # calls values only when requested.
This is similar with
range
andxrange
wherexrange
only calls values when requested.UPDATE: Do note that the
iteritems
,iterkeys
,itervalues
are removed from Python 3.x. Thedict.keys()
,dict.items()
anddict.values()
return views instead oflists
. http://docs.python.org/release/3.1.5/whatsnew/3.0.html#views-and-iterators-instead-of-lists -
Use
isinstance
rather thantype
Don’t do
if type(s) == type(""): ... if type(seq) == list or \ type(seq) == tuple: ...
rather:
if isinstance(s, basestring): ... if isinstance(seq, (list, tuple)): ...
For why not to do so: http://stackoverflow.com/a/1549854/504262
Notice I used
basestring
and notstr
as you might be trying to check if a unicode object is a string. For example:>>> a=u'aaaa' >>> print isinstance(a, basestring) True >>> print isinstance(a, str) False
This is because in Python versions below 3.0 there are two string types
str
andunicode
:object | | basestring / \ / \ str unicode
-
Learn the various
collections
Python has various container datatypes which are better alternative to the built-in containers like
list
anddict
for specific cases.Generally most use this:UPDATE: I’m sure most do not use this. Carelessness from my side. A few may consider writing it this way:
freqs = {} for c in "abracadabra": try: freqs[c] += 1 except: freqs[c] = 1
Some may say a better solution would be:
freqs = {} for c in "abracadabra": freqs[c] = freqs.get(c, 0) + 1
Rather go for the
collection
typedefaultdict
from collections import defaultdict freqs = defaultdict(int) for c in "abracadabra": freqs[c] += 1
Other collections
namedtuple() # factory function for creating tuple subclasses with named fields deque # list-like container with fast appends and pops on either end Counter # dict subclass for counting hashable objects OrderedDict # dict subclass that remembers the order entries were added defaultdict # dict subclass that calls a factory function to supply missing values
UPDATE: As noted by a few in Hacker News I could have used
Counter
instead ofdefaultdict
.>>> from collections import Counter >>> c = Counter("abracadabra") >>> c['a'] 5
-
When creating classes Python’s magic methods
__eq__(self, other) # Defines behavior for the equality operator, ==. __ne__(self, other) # Defines behavior for the inequality operator, !=. __lt__(self, other) # Defines behavior for the less-than operator, <. __gt__(self, other) # Defines behavior for the greater-than operator, >. __le__(self, other) # Defines behavior for the less-than-or-equal-to operator, <=. __ge__(self, other) # Defines behavior for the greater-than-or-equal-to operator, >=.
There are several others.
-
Conditional Assignments
x = 3 if (y == 1) else 2
It does exactly what it sounds like: “assign 3 to x if y is 1, otherwise assign 2 to x”. You can also chain it if you have something more complicated:
x = 3 if (y == 1) else 2 if (y == -1) else 1
Though at a certain point, it goes a little too far.
Note that you can use if … else in any expression. For example:
(func1 if y == 1 else func2)(arg1, arg2)
Here
func1
will be called if y is 1 andfunc2
, otherwise. In both cases the corresponding function will be called with arguments arg1 and arg2.Analogously, the following is also valid:
x = (class1 if y == 1 else class2)(arg1, arg2)
where
class1
andclass2
are two classes. -
Use the
Ellipsis
when necessary.UPDATE: As one commenter mentioned in Hacker News “Using Ellipsis for getting all items is a violation of the Only One Way To Do It principle. The standard notation is
[:]
.” I do agree with him. A better example is given using numpy in stackoverflow:The ellipsis is used to slice higher-dimensional data structures.
It’s designed to mean at this point, insert as many full slices (:) to extend the multi-dimensional slice to all dimensions.
Example:
>>> from numpy import arange >>> a = arange(16).reshape(2,2,2,2)
Now, you have a 4-dimensional matrix of order 2x2x2x2. To select all first elements in the 4th dimension, you can use the ellipsis notation
>>> a[..., 0].flatten() array([ 0, 2, 4, 6, 8, 10, 12, 14])
which is equivalent to
>>> a[:,:,:,0].flatten() array([ 0, 2, 4, 6, 8, 10, 12, 14])
Previous suggestion.
When creating a class you can use
__getitem__
to make you class’ object work like a dictionary. Take this class as an example:class MyClass(object): def __init__(self, a, b, c, d): self.a, self.b, self.c, self.d = a, b, c, d def __getitem__(self, item): return getattr(self, item) x = MyClass(10, 12, 22, 14)
Because of
__getitem__
you will be able to get the value ofa
in the objectx
byx['a']
. This is probably a know fact.This object is used to extend the Python slicing.(http://docs.python.org/library/stdtypes.html#bltin-ellipsis-object). Thus if we add a clause:
def __getitem__(self, item): if item is Ellipsis: return [self.a, self.b, self.c, self.d] else: return getattr(self, item)
We can use
x[...]
to get a list containing all the items.>>> x = MyClass(11, 34, 23, 12) >>> x[...] [11, 34, 23, 12]
http://blog.jobbole.com/19835/
围绕一门语言,学习它的文化精髓,能让你成为一名更优秀的程序员。如果你还没读过Python之禅(Zen of Python) ,那么打开Python的命令提示符输入import this,列表中的每一项你都可以在这里找到相对应的例子。
(Credit: itswater )
吸引我注意力的一条是:
优雅胜于丑陋 (Beautiful is better than ugly)
看下面例子:
一个带有数字参数的list函数其功能是返回参数中的奇数可以分开写:
1
2
3
4
5
6
|
#-----------------------------------------------------------------------
halve_evens_only
=
lambda
nums:
map
(
lambda
i: i
/
2
,\
filter
(
lambda
i:
not
i
%
2
, nums))
#-----------------------------------------------------------------------
def
halve_evens_only(nums):
return
[i
/
2
for
i
in
nums
if
not
i
%
2
]
|
记住Python中那些非常简单的事
两个变量的交换:
1
|
a, b
=
b, a
|
参数在切片操作中的步骤,如:
1
2
3
|
a
=
[
1
,
2
,
3
,
4
,
5
]
>>> a[::
2
]
# 以步长为2的增量迭代整个list对象
[
1
,
3
,
5
]
|
一个特殊的例子 `x[::-1]`用来反转x的实用语法。
1
2
|
>>> a[::
-
1
]
[
5
,
4
,
3
,
2
,
1
]
|
不要用可变对象作为默认参数值(Don’t use mutable as defaults)
1
2
3
4
|
def
function(x, l
=
[]):
# 不要这么干
def
function(x, l
=
None
):
# 更好的一种方式
if
l
is
None
:
l
=
[]
|
使用iteritems而不是items
iteriterms 使用的是 generators,所以当迭代很大的序列是此方法更好
1
2
3
4
5
|
d
=
{
1
:
"1"
,
2
:
"2"
,
3
:
"3"
}
for
key, val
in
d.items()
# 调用items()后会构建一个完整的list对象
for
key, val
in
d.iteritems()
# 只有在迭代时每请求一次才生成一个值
|
此情景和range与xrange的关系相似。
使用isinstance 而不是type
不要这样做:
1
2
3
|
if
type
(s)
=
=
type
(""): ...
if
type
(seq)
=
=
list
or
\
type
(seq)
=
=
tuple
: ...
|
应该是这样:
1
2
|
if
isinstance
(s,
basestring
): ...
if
isinstance
(seq, (
list
,
tuple
)): ...
|
至于为什么这样做,看这里:http://stackoverflow.com/a/1549854/504262
需要注意的是这里使用basestring而不是str是因为你可能会用一个unicode对象去检查是否为string,例如:
1
2
3
4
5
|
>>> a
=
u
'aaaa'
>>>
print
isinstance
(a,
basestring
)
True
>>>
print
isinstance
(a,
str
)
False
|
因为在Python中3.0以下的版本存在两种字符串类型str和unicode
object
|
basestring
/ \
str unicode
学习各种集合(learn the various collections)
python有各种各样的容器数据类型,在特定情况下选择python内建的容器如:list和dict。通常更多像如下方式使用:
1
2
3
4
5
6
|
freqs
=
{}
for
c
in
"abracadabra"
:
try
:
freqs[c]
+
=
1
except
:
freqs[c]
=
1
|
一种更好的方案如下:
1
2
3
|
freqs
=
{}
for
c
in
"abracadabra"
:
freqs[c]
=
freqs.get(c,
0
)
+
1
|
一种更好的选择 collection类型defautdict:
1
2
3
4
|
from
collections
import
defaultdict
freqs
=
defaultdict(
int
)
for
c
in
"abracadabra"
:
freqs[c]
+
=
1
|
其它集合
1
2
3
4
5
|
namedtuple()
# 用指定的域创建元组子类的工厂函数
deque
# 类似list的容器,快速追加以及删除在序列的两端
Counter
# 统计哈希表的dict子类
OrderedDict
# 记录实体添加顺序的dict子类
defaultdict
# 调用工厂方法为key提供缺省值的dict子类
|
当创建类时Python的魔术方法:
1
2
3
4
5
6
|
__eq__(
self
, other)
# 定义相等操作的行为, ==.
__ne__(
self
, other)
# 定义不相等操作的行为, !=.
__lt__(
self
, other)
#定义小于操作的行为, <.
__gt__(
self
, other)
#定义不大于操作的行为, >.
__le__(
self
, other)
#定义小于等于操作的行为, <=.
__ge__(
self
, other)
#定义大于等于操作的行为, >=.
|
条件赋值
1
|
x
=
3
if
(y
=
=
1
)
else
2
|
表达式请起来恰恰像:如果y等于1就把3赋值给x,否则把2赋值给x,当然同样可以使用链式条件赋值如果你还有更复杂的条件的话。
1
|
x
=
3
if
(y
=
=
1
)
else
2
if
(y
=
=
-
1
)
else
1
|
然而到了某个特定的点,它就有点儿过分了。
记住,你可以在任何表达式中使用if-else例如:
1
|
(func1
if
y
=
=
1
else
func2)(arg1, arg2)
|
func1将被调用如果y等于1的话,反之func2被调用。两种情况下,arg1和arg2两个参数都将附带在相应的函数中。
类似地,下面这个表达式同样是正确的
1
|
x
=
(class1
if
y
=
=
1
else
class2)(arg1, arg2)
|
class1和class2是两个类
在有必要的时侯使用Ellipsis
创建类时,你可以使用__getitem__,让你的类像字典一个工作,拿下面这个类举例来说:
1
2
3
4
5
6
7
8
|
class
MyClass(
object
):
def
__init__(
self
, a, b, c, d):
self
.a,
self
.b,
self
.c,
self
.d
=
a, b, c, d
def
__getitem__(
self
, item):
return
getattr
(
self
, item)
x
=
MyClass(
10
,
12
,
22
,
14
)
|
因为有了__getitem__,你就能够通过对象x的x[‘a’]获取a的值,这应该是公认的事实。
这个对象通常用于继承Python的切片(slicing) (http://docs.python.org/library/stdtypes.html#bltin-ellipsis-object),如果添加如下语句:
1
2
3
4
5
|
def
__getitem__(
self
, item):
if
item
is
Ellipsis:
return
[
self
.a,
self
.b,
self
.c,
self
.d]
else
:
return
getattr
(
self
, item)
|
我们就可以使用x[…]获取的包含所有项的序列
1
2
3
|
>>> x
=
MyClass(
11
,
34
,
23
,
12
)
>>> x[...]
[
11
,
34
,
23
,
12
]
|
原文:Satyajit Ranjeev 编译:伯乐在线 – 刘志军
【如需转载,请标注并保留原文链接、译文链接和译者等信息,谢谢合作!】