Python编程中需要注意的一些事

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.html

    One 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 than items

    iteritems uses generators 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 and xrange where xrange only calls values when requested.

    UPDATE: Do note that the iteritems, iterkeys, itervalues are removed from Python 3.x. The dict.keys(), dict.items() and dict.values() return views instead of lists. http://docs.python.org/release/3.1.5/whatsnew/3.0.html#views-and-iterators-instead-of-lists

  • Use isinstance rather than type

    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 not str 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 and unicode:

            object
               |
               |
           basestring
              / \
             /   \
           str  unicode
  • Learn the various collections

    Python has various container datatypes which are better alternative to the built-in containers like list and dict 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 type defaultdict

      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 of defaultdict.

      >>> 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 and func2, 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 and class2 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 of a in the object x by x['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  编译:伯乐在线 – 刘志军

【如需转载,请标注并保留原文链接、译文链接和译者等信息,谢谢合作!】


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值