python 随笔

python 学习笔记html

运算符重载

PYTHON-进阶-魔术方法小结(方法运算符重载)
python有着像C++类似的运算符重载,只须要在类中重写__add__、sub 等方法,就能够直接对对象进行 + - 等操做,就好像内置对象同样。也能够重写__getattr__、__setattr__等方法来操做属性,initdelstr 、__len__等基本方法均可以重载,比较符的重载包括cmplt、__gt__等,以及getitem、__setitem__等操做索引的方法。总之,彻底能够经过重载将一个类写的python之父都不认得。python

coroutine

传统咱们实现消费者 生产者模型 都是须要经过多线程之间 互相协做,一个线程生产,一个线程消费。协程就是将多线程的事情在一个线程里面干了。传统咱们一个函数老是一个入口,一个出口,很明确。协程就是容许一个子程序内部能够中断,而后执行其余子程序,在适当的时候再返回来继续执行,即便在while True这样的函数。这样的话咱们就能够生产 后就调用消费者,而后消费后再继续生产。
python 2.x中实现协程的方式 是经过generator,yield 中止一个子程序后,能够经过send(xxx) 继续执行
协程的好处:sql

  1. 极高的效率,不须要操做系统切换线程,只是程序内部控制segmentfault

    由于协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可得到极高的性能。数组

  2. 并且也不须要多线程的锁机制。。。由于这根本就只有一个线程嘛
  3. 经过协程也能够将一个复杂的程序分红多个相互协做的子程序,就好像unix 的管道同样,read ---> grep ---> print 这样,程序会更简单明了,复用性更高多线程

# 简洁高效的生产消费模式

# 装饰器, 用来自动启动协程
def coroutine(func):
    def go(*args, **kwargs):
        g = func(*args, **kwargs)
        g.send(None)
        return g
    return go


@coroutine
def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print ('consume {}'.format(n))
        time.sleep(1)
        r = '200 OK'


def producer(target):
    while True:
        n = random.randint(1, 100)
        print ('produce {}'.format(n))
        r = target.send(n)
        print ('consumer return {}'.format(r))


if __name__ == '__main__':
    producer(consumer())

generator

generator 跟普通的方法差很少,只是能够经过yield返回多个值,以下闭包

def gen():
    yield 1
    yield 2

g = gen()
print g # 输出<generator object gen at 0x00000000026BCFC0>
print g.next() # 输出1
print g.next() # 输出2
print g.next() # StopIteration 异常

经过dir(g) 能够看到generator 实现了__iter__ 和 next 方法,因此generator 也是一个迭代器, 生成器能够用来简化迭代器的实现app

# 简化版的小写字母生成器,相对自定义迭代器来讲,真的简单多了
def lower_letters():
    current = 'a'
    while current <= 'z':
        yield current
        current = chr(ord(current) + 1)

for letter in lower_letters():
    print letter

iterator & iterable

python 中的for in语法,能够遍历iterable 对象,例如list,array,map等,其实就是用iter()构造了一个iterator,并捕获StopIteration异常dom

_iter = iter(iterableObj)
while True:
    try:
        x = _iter.next()
    except StopIteration:
        break
    do_your_func(x)

iterator 和 iterable 本质上的区别是 iterator 实现了__next__(python2.x 是next)和 __iter__方法,而iterable只实现了__iter__方法,经过dir([1,2]) 能够看到数组中只实现了iter ,只是iterable对象函数

因此,咱们能够自定义一个迭代器,只要实现了__next__ 和 iter,而且__next__ 抛出StopIteration异常

class UpperLetter(object):
    def __init__(self):
        self.current = 'A'

    def next(self):
        if self.current > 'Z':
            raise StopIteration()

        result = self.current
        self.current = chr(ord(self.current) + 1)
        return result

    def __iter__(self):
        return self


letters = UpperLetter()
# 自定义的迭代器能够经过for in遍历哦
for letter in letters:
    print (letter)

下划线

方法先后双下划线

约定这是python中的特殊方法,一般你将覆盖这些方法,实现所需的功能,例如__init__

方法前单下划线

约定这是私有方法,外部不能访问。
对于解释器来讲, from <模块 包名> import * 是没法导入以_开头的方法

拷贝

python都对象之间赋值都是拷贝引用,若是要拷贝对象,须要使用copy模块

  1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
  2. copy.deepcopy 深拷贝 拷贝对象及其子对象
userInfo = {
    "resultCode": 0,
    "users": {
        'name': "jobs"
    }
}

a = userInfo
b = copy.copy(userInfo)
c = copy.deepcopy(userInfo)

# 修改原来的对象
userInfo['resultCode'] = 1
userInfo['users']['name'] = 'ryan'

print a
print b
print c
# 输出:
# {'resultCode': 1, 'users': {'name': 'ryan'}}
# {'resultCode': 0, 'users': {'name': 'ryan'}}
# {'resultCode': 0, 'users': {'name': 'jobs'}}

能够看到,传引用的全改了,浅拷贝的话子对象仍是保留着原来的引用,因此子对象跟着改了,深拷贝岿然不动!

切片

切片是python一个颇有趣的语法糖,提供了简洁的方法来取一个数组的一部分

L=[1, 2, 3,4,5]
print L[1: 3] # 取索引1到索引3,不包含索引3
print L[:] # 第一个参数不填表示第一位,最后一个参数不填表示取最后一位
print L[-2:] # 负数表示倒数第n个元素
print L[::2] # 最后一个参数表示隔几个取一次,跟range相似

# 输出
# [2, 3]
# [1, 2, 3, 4, 5]
# [4, 5]
# [1, 3, 5]

# 优雅地将数组 进行分组操做,这里若是i + 3大于数组len, 切片也能自动切到最后一截哈哈
for i in range(0, len(L), 3):
    print L[i: i+3]
# 输出
# [1, 2, 3]
# [4, 5]

闭包

定义

父函数中返回的函数引用了父函数中的参数或者局部变量,这个函数就称为闭包。所以,闭包也是携带状态的函数,而且它的状态能够对外隐藏起来。闭包之于父函数就好像实例之于类

闭包修改外部变量?

通常来讲闭包不能修改外部变量,由于对于同名变量的修改 python 把它看作了 子函数中的 局部变量,因此你直接修改会返回UnboundLocalError: local variable 'm' referenced before assignment错误(听说在python3中能够用nolocal 声明外部变量便可修改)

闭包实现的原理

闭包函数相对于普通函数多出了一个__closure__属性,这是一个元组,这个元祖里面包含了全部闭包函数中引用到的外部变量

参考:
说说 Python 中的闭包


装饰器

装饰器就是一种特殊的闭包,经过传递一个待修饰的函数 给 修饰函数,并返回一个修饰后的函数,能够用@decoratorFunc 来简化语法,装饰器的做用就是为已经存在的函数或对象添加额外的功能

参考:
Python 的闭包和装饰器:https://segmentfault.com/a/1190000004461404
详解 Python 的装饰器

列表生成式/字典生成式(简洁美观)

[ x * x for x in list] & { x[a] : x[b] for x in list}

python 变量做用域

  1. python的做用域是一层一层向外查的,可是若是在内层想要改变外层的变量,就须要声明global,不然只是至关于定义了一个覆盖全局的局部变量,以下
var = 1

def func1():
    # 此处声明 global var 解决
    var = var + 1
    return var

print func1() 
# 报错UnboundLocalError: local variable 'var' referenced before assignment
# 由于咱们企图在内层改变外层的var,因此python就把他当作局部变量了,那很明显,这个var在局部并无赋值
  1. 一样的,对于闭包,也是尽可能不要在闭包中修改外部函数的变量,理由同上(能够声明nonlocal)
  2. 除了def/class/lambda以外,其余如:if/else try/except for/while并不能改变其做用域. 略坑啊,定义在这些东西里面的变量,外部是能够访问的。。。

with as 字句

用with as 字句代替以前的try .... finally fs.close()之类的语句方便了不少

tuple

tuple 就是不可变的list, 用(1, 2, 3)定义
**若是要定义只有一个元素的tuple,不能定义为(1),python把他当作数字1(括号运算),正确的写法为(1, )

参数

默认参数的坑

python 的默认参数若是为数组的话,要千万当心了

def func2(array=[]):
    array.append("haha")
    print array

func2()  # 输出 ['haha']
func2()  # 输出 ['haha', 'haha']

为何每次调用的结果都不同? 由于默认参数的值在函数定义的时候就生成好了,因此当默认参数指向的是 可变对象如list
就会改变它的值,因此**默认参数必定要是不可变对象"",如str,int等

可变参数

要往可变参数中传入一个数组,有两种方法

def func3(*arr):
    for i in arr:
        print i

nums = [1, 2, 3]
func3(nums[0], nums[1], nums[2])  # 不得不说,这种写法巨丑
func3(*nums)  # 好看

关键字参数

可变参数在方法中就是一个tuple,而关键字参数在方法中就是一个dict,它能够扩展函数的功能,让用户自定义配置,例如sqlalchemy的create_engine(*args, kwargs)就使咱们能够根据须要定义encoding,echo等参数
一样的,要传入一个dict给可变参数的方法能够用**dict**

星解

星解提供了一个很艺术化的方法来unpack一个list或dict

def test(x, y, z):
    print(x, y, z)
 
testDict = {'x': 1, 'y': 2, 'z': 3} 
testList = [10, 20, 30]
 
test(*testDict)
test(*testList)
 
#1-> x y z
#2-> 10 20 30
相关文章
相关标签/搜索