• python 随笔


    python 学习笔记

    运算符重载

    PYTHON-进阶-魔术方法小结(方法运算符重载)
    python有着像C++相似的运算符重载,只需要在类中重写__add__、sub 等方法,就可以直接对对象进行 + - 等操作,就好像内置对象一样。也可以重写__getattr__、__setattr__等方法来操作属性,initdelstrlen__等基本方法都可以重载,比较符的重载包括__cmpltgt__等,以及__getitem、__setitem__等操作索引的方法。总之,完全可以通过重载将一个类写的python之父都不认得。

    coroutine

    传统我们实现消费者 生产者模型 都是需要通过多线程之间 互相协作,一个线程生产,一个线程消费。协程就是将多线程的事情在一个线程里面干了。传统我们一个函数总是一个入口,一个出口,很明确。协程就是允许一个子程序内部可以中断,然后执行其他子程序,在适当的时候再返回来继续执行,即使在while True这样的函数。这样的话我们就可以生产 后就调用消费者,然后消费后再继续生产。
    python 2.x中实现协程的方式 是通过generator,yield 停止一个子程序后,可以通过send(xxx) 继续执行
    协程的好处:

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

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

    1. 而且也不需要多线程的锁机制。。。因为这根本就只有一个线程嘛
    2. 通过协程也可以将一个复杂的程序分成多个相互协作的子程序,就好像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 也是一个迭代器, 生成器可以用来简化迭代器的实现

    # 简化版的小写字母生成器,相对自定义迭代器来说,真的简单多了
    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异常

    _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
    
  • 相关阅读:
    linux—上传,下载本地文件到服务器
    elasticsearch摸石头过河——数据导入(五)
    elasticsearch摸石头过河——配置文件解析(四)
    elasticsearch摸石头过河——基本安装应用(三)
    elasticsearch摸石头过河——常用数据类型(二)
    spring AOP——名词,语法介绍(一)
    EXCEL(POI)导入导出工具类
    MAVEN 排除第三方jar
    elasticsearch摸石头过河——基本概念(一)
    websocket应用
  • 原文地址:https://www.cnblogs.com/kaixuanguilai/p/7773969.html
Copyright © 2020-2023  润新知