Python tips

本文总结了Python编程中的一些小知识,包括lambda匿名函数、生成器、装饰器、列表字典集合推导、字典使用、Python隐藏特性和数据结构算法等。详细介绍了各种功能的实现方式和应用场景。

I am not a creator. I am just a porter.

简介:对一些python的小知识进行总结

#### lambda匿名函数 #### {#lambda}

lambda函数也叫匿名函数,也就是函数没有名称。先看一个简单的例子:

def add(x, y):
    return x + y

对应的lambda函数则如下:

add = lambda x, y: x + y

我们在很多情况下都可以看到lambda表达式,它并不是python特有的,而且大多数情况下我们并不会使用它。那么,在什么情况下使用lambda函数呢?由于lambda没有函数名这样不会产生很多函数的引用,这样对代码的清晰是有好处的,个人认为,当一个功能简单的函数,只在某个局部使用而且使用的次数不多,我们可以使用lambda函数,当然,不用lambda是完全可以的。

#### python生成器 #### {#generator}

什么是生成器?在谈这个问题之前我们来看看python的迭代器。当我们可以逐项去读取的对象的属性时,我们就说这个对象是可迭代的:

>>> clist = [1, 2, 3, 4]
>>> for item in clist:
...     print item
...     
1
2
3
4
>>> for item in clist:
...     print item
...     
1
2
3
4
>>> 

clist是一个可迭代的对象,可以使用for-in语法来访问每个item。我们常见的迭代器:list,tuple,dict,file... for语句在容器对象中调用iter(),该函数调用对象的__iter__()函数返回一个定义了next()方法的迭代器对象:

class Array(object):
    def __init__(self):
        self._list = [i for i in range(10)]
        self.ops = -1

    def __iter__(self):
        self.ops = -1
        return self

    def next(self):
        self.ops += 1
        if self.ops == len(self._list):
            raise StopIteration()

        return self._list[self.ops]

__iter__()和next()不必要在同一个类,我们只要明白,__iter__()是返回一个可迭代的对象,而可迭代的对象包含next(),记住越界raise StopIteration:

class Array(object):
    def __init__(self):
        self._list = [i for i in range(10)]

    def __iter__(self):
        return ArrayIterator(self)

class ArrayIterator(object):
    def __init__(self, array):
        self._array = array
        self._ops = -1

    def next(self):
        self._ops += 1
        if self._ops == len(self._array._list):
            raise StopIteration()

        return self._array._list[self._ops]

说完迭代器,我们来看看生成器,首先生成器是可以迭代的,但是只可以读取一次,因为它并不把所有的值放在内存中,而是实时生成数据:

>>> gen = (i for i in range(10))
>>> for ite in gen:
...     print ite
...       
0
1
2
3
4
5
6
7
8
9
>>> for ite in gen:
...     print ite
... 
>>> 

比较code-2.1和code-2.4,发现当我们把[]换成()后,for...in...只能使用一次,这就是因为生成器只能被迭代一次。

如何构造一个生成器?我们使用yield关键字。yield是一个类似return的关键字,只是这个函数返回的是一个生成器:

def generator(init_list):
for i in init_list:
    yield i

gen = generator([i for i in range(3)])

for i in gen:
    print i # out 0 1 2

print gen.next() # raise StopIteration

首先当我们调用generator这个函数的时候,它并不是立马就执行,这个函数返回一个生成器对象(包含next()方法)。函数内的代码则是当我们使用for循环的时候执行。如果生成器内部没有定义yield,那么这个生成器被认为成空,不满足循环条件。

#### python装饰器 #### {#decorator}

装饰器,用于装饰一个函数,所谓的装饰就是在原有的基础上增加功能(参数检查,编码解码,添加日志...)

def help():
    """
        一个请求帮助的函数
    """
    print("Can you help me?")

def decorator(func):
    """
        在请求帮助前,打招呼
    """
    print("Hi, Mike!")
    return func #这里要返回原函数对象,否则外面无法调用

def decorator_with_funcparams(args):
    """
        装饰器和被装饰的函数都是带参数的,args是装饰器的参数,func_param是被装饰的函数的参数
    """
    def _deco(func):
        def func_deco(func_param):
            print("Hi, " + func_param)
            return func()
        return func_deco
    return _deco

装饰器测试:

hp = decorator(help)
hp()
hp()

output:
----------------
Hi, Mike!
Can you help me?
Can you help me?

装饰器只装饰一次,所以Hi,Mike!输出一次,在使用@decorator也是一样

@decorator
def hp():
    help()

hp()
hp()

output:
----------------
Hi, Mike!
Can you help me?
Can you help me?

为了使得每次调用的时候,都能被装饰,我们需要在装饰器使用内嵌函数:

def decorator_with_inner(func):
    def _deco():
        print("Hi, Mike!")
        return func() #这里调用函数,而不是返回原函数的对象
    return _deco

@decorator_with_inner
def hp():
    help()

hp() #实际上调用的是_deco()
hp()

output:
----------------
Hi, Mike!
Can you help me?
Hi, Mike!
Can you help me?

有时候装饰器是根据上下文来确定装饰动作,这个是就需要带参数了:

def decorator_with_params(args):
    """
        带有参数的装饰器,由于有参数,所以在函数调用时只会使用应用时的参数
        而不是接收被装饰的函数作为参数,所以必须在其内部再创建一个函数
    """
    print(args)
    def _deco(func):
        print("Hi, Mike")
        return func #这里不能使用func()
    return _deco

@decorator_with_params(args = 'Please!')
def hp():
    help()

hp() #调用的是func(),不是_deco()
hp()

output:
----------------
Please!
Hi, Mike!
Can you help me?
Can you help me?

这里可以看出装饰器(传入被装饰函数对象的函数以及外层的函数)只会执行一遍,对于这个例子来说,print(args)和_deco函数都只是执行了一遍。当我们调用hp()的时候实际上调用的是我们传入func时的函数的返回值,所以在那一层不要调用函数,而是返回函数对象。所以为了保证每次都执行装饰动作我们需要在传入func的函数中内嵌一个函数:

def decorator_with_params2(args):
    """
        带有参数的装饰器,由于有参数,所以在函数调用时只会使用应用时的参数
        而不是接收被装饰的函数作为参数,所以必须在其内部再创建一个函数
    """
    print(args)
    def _deco(func):
        def f_deco():
            print("Hi, Mike")
            return func() #注意这里是调用函数,而不是返回原函数对象
        return f_deco
    return _deco

@decorator_with_params(args = 'Please!')
def hp():
    help()

hp() #调用的是f_deco(),不是_deco()
hp()

output:
----------------
Please!
Hi, Mike
Can you help me?
Hi, Mike
Can you help me?

当被装饰的函数需要传入参数,在内嵌函数中带上参数就可以了:

def decorator_with_funcparams(args):
"""
    装饰器和被装饰的函数都是带参数的,args是装饰器的参数,func_param是被装饰的函数的参数
"""
def _deco(func):
    def func_deco(func_param):
        print("Hi, " + func_param)
        return func(func_param)
    return func_deco
return _deco

@decorator_with_funcparams(args = 'Please!')
def hp(func_param):
    print("Can you help me, " + func_param + "?")

hp("Mike1") 
hp("Mike2")

output:
-----------------------
Hi, Mike1
Can you help me, Mike1?
Hi, Mike2
Can you help me, Mike2?

调用hp("Mike1")实际上就是调用func_deco("Mike1"),所以在func_deco()函数体中是调用func(),而不是返回函数对象。如果我们返回的是函数对象会怎么样:

def decorator_with_funcparams(args):
    """
        装饰器和被装饰的函数都是带参数的,args是装饰器的参数,func_param是被装饰的函数的参数
    """
    def _deco(func):
        def func_deco(func_param):
            print("Hi, " + func_param)
            return func
        return func_deco
    return _deco

@decorator_with_funcparams(args = 'Please!')
def hp(func_param):
    print("Can you help me, " + func_param + "?")

hp("Mike1")("test1")
hp("Mike2")("test2") 

output:
----------------------- 
Hi, Mike1
Can you help me, test1?
Hi, Mike2
Can you help me, test2?

我们调用hp()也就是调用的func_deco(),所以返回的是真正的hp对象,hp()()这个时候就是对真正的hp进行调用。但是这样就改变了hp的行为了,所以除了特殊情况下,一般不这么做!

Python装饰器(decorator)在实现的时候,有一些细节需要被注意。例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。这样有时候会对程序造成一些不便,例如笔者想对unittest框架中的一些函数添加自定义的decorator,添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响。所以,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functools的wrap,它能保留原有函数的名称和docstring。

def authenticated(method):   
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
            if self.request.method in ("GET", "HEAD"):
                url = self.get_login_url()
                if "?" not in url:
                    if urlparse.urlsplit(url).scheme:
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
                        next_url = self.request.uri
                    url += "?" + urlencode(dict(next=next_url))
                self.redirect(url)
                return
            raise HTTPError(403)
        return method(self, *args, **kwargs)
    return wrapper
    

#### python列表,字典,集合推导 #### {#derive}

在python的使用过程中,我们经常需要构造一个列表。列表推导为我们提供了一个简单的方法:

clist = [x for x in range(100) if x % 2 == 0]

使用一句话轻松生成一个100以内的偶数列表。

在python 3.1(反向移植到python 2.7)之后,集合(不是元组)和字典也支持推导式:

>>> { x for x in range(10) if x % 2 == 0}
set([0, 8, 2, 4, 6])
>>> { x: x * 2 for x in range(10) if x % 2 == 0}
{0: 0, 8: 16, 2: 4, 4: 8, 6: 12}

注意:(x for x in range(10) if x % 2 == 0) 得到的是一个生成器

#### python字典的使用 #### {#dictionary}

判断键的存在性

丑陋的:

dic.has_key(key)

python的做法:

key in dic

丑陋的:

not key in dic

难道这样不是更好:

key not in dic

字典取值

丑陋的:

if key not in dic:
    dic[key] = 0
dic[key] = dic[key] + 1

这样更简洁:

dic[key] = dic.get(key, 0) + 1

统计计数

统计字典重复的次数:

>>> from collections import Counter
>>> Counter([1,1,1,1,2,1,2,3,2,3])
Counter({1: 5, 2: 3, 3: 2})

字典的初始化

有时你的字典里都是经常修改的对象,你需要初始化一些数据到这个字典,也需要修改其中的一些值。比如说,你在维护一个这样的dict:它的值都是链表。

常见的:

dct = {}
for (key, value) in data:
    if key in dct:
        dct[key].append(value)
    else:
        dct[key] = [value]

好一点:

dct = {}
for (key, value) in data:
    group = dct.setdefault(key, []) # key might exist already
    group.append(value)

setdefault,如果存在,返回dct[key],不存在就设为default,并返回。

使用defaultdict:

dct = defaultdict(list)
for (key, value) in data:
    dct[key].append(value) # all keys have a default already

defaultdict,对于每生成一对新的key-value,就会给value一个默认值,这个默认值就是defaultdict的参数。

#### Python的隐藏特性 ####

以下介绍来自PyZh

函数参数unpack

def foo(x, y):
    print x, y

alist = [1, 2]
adict = {'x': 1, 'y': 2}

foo(*alist)  # 1, 2
foo(**adict)  # 1, 2

链式比较操作符

>>> x = 3
>>> 1 < x < 5
True
>>> 4 > x >=3
True

注意函数的默认参数

>>> def foo(x=[]):
...     x.append(1)
...     print x
...
>>> foo()
[1]
>>> foo()
[1, 1]

更安全的做法:

>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
...
>>> foo()
[1]
>>> foo()
[1]
>>>

字典有个get()方法

dct.get(key[, default_value]) , 当字典 dct 中找不到 key 时, get 就会返回 default_value

sum[value] = sum.get(value, 0) + 1

带关键字的格式化

>>> print "Hello %(name)s !" % {'name': 'James'}
Hello James !
>>> print "I am years %(age)i years old" % {'age': 18}
I am years 18 years old

更新些的格式化:

>>> print "Hello {name} !".format(name="James")
Hello James !

快有些模板引擎的味道了:)

for...else 语法

>>> for i in (1, 3, 5):
...     if i % 2 == 0:
...         break
... else:
...     print "var i is always an odd"
...
var i is always an odd
>>>

else 语句块会在循环结束后执行,除非在循环块中执行 break

**dict 的特殊方法__missing__**

Python 2.5之后引入的。当查找不到 key 的时候,会执行这个方法。

>>> class Dict(dict):
...   def __missing__(self, key):
...     self[key] = []
...     return self[key]
...
>>> dct = Dict()
>>> dct["foo"].append(1)
>>> dct["foo"].append(2)
>>> dct["foo"]
[1, 2]

这很像 collections.defaultdict 不是吗?

>>> from collections import defaultdict
>>> dct = defaultdict(list)
>>> dct["foo"]
[]
>>> dct["bar"].append("Hello")
>>> dct
defaultdict(<type 'list'>, {'foo': [], 'bar': ['Hello']})

切片操作的步长参数

还能用步长 -1 来反转链表:

>>> a = [1, 2, 3, 4, 5]
>>> a[::2]
[1, 3, 5]
>>> a[::-1]
[5, 4, 3, 2, 1]
>>>
9.9. 另一种字符串连接
>>> Name = "Wang" "Hong"
>>> Name
'WangHong'

连接多行:

>>> Name = "Wang" \
...  "Hong"
>>> Name
'WangHong'

**Python解释器中的”_”**

>>> range(4)
[0, 1, 2, 3]
>>> _
[0, 1, 2, 3]

_ 即Python解释器上一次返回的值

嵌套列表推导式

>>> [(i, j) for i in range(3) for j in range(i)]
[(1, 0), (2, 0), (2, 1)]

try/except/else

try:
  put_4000000000_volts_through_it(parrot)
except Voom:
  print "'E's pining!"
else:
  print "This parrot is no more!"
finally:
  end_sketch()

print 重定向输出到文件

>>> print >> open("somefile", "w+"), "Hello World"

注意打开的模式: "w+" 而不能 "w" , 当然 "a" 是可以的

省略号

在 Python 3 中你可以直接使用省略号这个文法:

Python 3.2 (r32:88445, Oct 20 2012, 14:09:50)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> ...
Ellipsis
Python2 中呢?

>>> class C(object):
...  def __getitem__(self, item):
...   return item
...
>>> C()[1:2, ..., 3]
(slice(1, 2, None), Ellipsis, 3)
>>>

Python3中的元组unpack

>>> a, b, *rest = range(10)
>>> a
0
>>> b
1
>>> rest
[2, 3, 4, 5, 6, 7, 8, 9]
>>>

当然也可以取出最后一个:

>>> first, second, *rest, last = range(10)
>>> first
0
>>> second
1
>>> last
9
>>> rest
[2, 3, 4, 5, 6, 7, 8]

pow()还有第三个参数

我们都知道内置函数 pow, pow(x, y) 即 x ** y

但是它还可以有第三个参数:

>>> pow(4, 2, 2)
0
>>> pow(4, 2, 3)
1

其实第三个参数是来求模的: pow(x, y, z) == (x ** y) % z

注意,内置的 pow 和 math.pow 并不是一个函数,后者只接受2个参数

enumerate还有第二个参数

enumerate 很赞,可以给我们索引和序列值的对, 但是它还有第二个参数:

>>> lst = ["a", "b", "c"]
>>> list(enumerate(lst, 1))
[(1, 'a'), (2, 'b'), (3, 'c')]

这个参数用来: 指明索引的起始值

显式的声明一个集合

新建一个集合,我们会:

>>> set([1,2,3])

在Python 2.7 之后可以这么写了:

>>> {1,2,3}
set([1, 2, 3])

用切片来删除序列的某一段

>>> a = [1, 2, 3, 4, 5, 6, 7]
>>> a[1:4] = []
>>> a
[1, 5, 6, 7]

当然用 del a[1:4] 也是可以的

去除偶数项(偶数索引的):

>>> a = [0, 1, 2, 3, 4, 5, 6, 7]
>>> del a[::2]
>>> a
[1, 3, 5, 7]

isinstance可以接收一个元组

这个真的鲜为人知, 我们可以用 isinstance(x, (float, int)) 来判断 x 是不是数:

>>> isinstance(1, (float, int))
True
>>> isinstance(1.3, (float, int))
True
>>> isinstance("1.3", (float, int))
False

那么对于第三个测试,你把 str 加入元组就可以看到这是怎么回事了:

>>> isinstance("1.3", (float, int, str))
True

也就是那个元组里面是 或 的关系,只要是其中一个的实例就返回 True

字典里的无限递归

>>> a, b = {}, {}
>>> a['b'] = b
>>> b['a'] = a
>>> a
{'b': {'a': {...}}}

当然你可以制作一个链表中的无限循环:

>>> a, b =  [], []
>>> a.append(b)
>>> b.append(a)
>>> a
[[[...]]]

真心不知道有什么用,不过蛮好玩的不是吗

Python可以认识Unicode中的数字

所以说,Python很赞:

>>> int(u'1234')
1234

不只是ASCII字符串的可以认出来,连Unicode的也可以。

不能访问到的属性

>>> class O(object):pass
...
>>> o = O()
>>> setattr(o, "can't touch this", 123)
>>> o.can't touch this
  File "<stdin>", line 1
    o.can't touch this
                     ^
SyntaxError: EOL while scanning string literal
>>>

不过,能用 setattr 设置属性,就可以用 getattr 取出

转载于:https://www.cnblogs.com/cgpjblog/p/4076223.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值