python 一些重要的知识

一、高级特性——生成器,迭代器

生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
def triangles():
    l1 = [1]
    yield l1
    l = [1, 1]
    yield l
    while True:
        l = l1 + [l[i] + l[i+1] for i in range(len(l) - 1)] + l1
        yield l
if __name__ == '__main__':
     t = triangles()
     for i in range(10):
         print(next(t))
     f = fib(15)
     for i in range(15):
         print(next(f))

#详见博文:https://blog.csdn.net/weixin_48127633/article/details/127667100

二、函数式编程

1. map, reduce ,filter, sorted

#map reduce函数
def fn(x, y):
    return x * 10 + y
print(reduce(fn, [1,3,5,7,9]))
#filter函数
print(list(filter(f, [1,2,3,4])))

2. 闭包

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。

def fun(*args):
    def fun_sum():
        sum_ = 0
        for i in args:
            sum_ += i
        return sum_
    return fun_sum
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs
    f = fun(1,2,3,4,5)
    print(f())
    f1, f2, f3 = count()
    print(f1(), f2(), f3())

3. 匿名函数

f = lambda n: n % 2 == 1

4. 装饰器

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
@log
def now():
    print('2015-3-25')
now()

5. 偏函数

functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

int2 = functools.partial(int, base = 2)
print(int2('1010101'))

6. 函数与作用域

运行python3 hello.py获得的sys.argv就是[‘hello.py’]
运行python3 hello.py Michael获得的sys.argv就是[‘hello.py’, ‘Michael’]

#外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public

def _private_1(name):
    return 'Hello, %s' % name
def _private_2(name):
    return 'Hi, %s' % name
def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

三、面向对象的高级编程

1. __slots__和 @property

slots__允许我们声明并限定类成员,并拒绝类创建__dict__和__weakref__属性以节约内存空间 ;
子类未声明__slots__时,不继承父类的__slots
,即此时子类实例可以随意赋值属性;
子类声明__slots__时,继承父类的__slots__,即此时子类的__slots__为其自身+父类的__slots__;
优点:
(1)更快的赋值属性;
(2)节约内存;

我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改

class DataSet(object):
    def __init__(self):
        self._images = 1
        self._labels = 2 #定义属性的名称
    @property
    def images(self): #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户有没办法随意修改。
        return self._images 
    @property
    def labels(self):
        return self._labels
l = DataSet()

#用户进行属性调用的时候,直接调用images即可,而不用知道属性名_images,因此用户无法更改属性,从而保护了类的属性。
print(l.images) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。

2. 多重继承

3. 定制类

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name: %s)' % self.name
print(Student('Michael'))
>>>Student object (name: Michael)
s = Student('Michael')
s
<__main__.Student object at 0x109afb310>

#直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的
getattr()
只有在没有找到属性的情况下,才调用__getattr__,已有的属性,不会在__getattr__中查找;
call()
任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用;

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

class Student(object):
    def __init__(self, name):
        self.name = name
    def __call__(self):
        print('My name is %s.' % self.name)
s = Student('Michael')
s() # self参数不要传入
>>>My name is Michael.

4. 枚举类

from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员

5. 元类

元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类;(type(), class())

def fn(self, name='world'): # 先定义函数
    print('Hello, %s.' % name)
    
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
h.hello()
>>> Hello, world.
print(type(Hello))
>>> <class 'type'>
print(type(h))
>>> <class '__main__.Hello'>

要创建一个class对象,type()函数依次传入3个参数:

class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
__metaclass__属性
Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类

四、异常处理

1. 错误处理

try:
    print('try...')
    r = 10 / int('a')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')

raise语句如果不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型

try:
    10 / 0
except ZeroDivisionError:
    raise ValueError('input error!')

2. 调试

(1)断言(assert)
def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n
def main():
    foo('0')

assert的意思是,表达式n != 0应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。

如果断言失败,assert语句本身就会抛出AssertionError

启动Python解释器时可以用-O参数来关闭assert

(2)使用logging模块
import logging
logging.basicConfig(level=logging.INFO)

logging允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了

import logging
def foo(s):
    return 10 / int(s)
def bar(s):
    return foo(s) * 2
def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)
main()
print('END')
(3)pdb

第4种方式是启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态;

pdb.set_trace() 只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点;

五、IO编程

1. 读写文件
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
2. StringIO和BytesIO

StringIO就是在内存中读写str,getvalue()方法用于获得写入后的str;

要读取StringIO,可以用一个str初始化StringIO,然后,像读文件一样读取

>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!

StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO
#请注意,写入的不是str,而是经过UTF-8编码的bytes

>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
3. 操作文件和目录

os.path.join(path1,path2) 合并目录
os.path.split(path) 拆分目录
os.path.splitext(path) 获取文件扩展名

4. 序列化

把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling;

>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

dumps是将dict转化成str格式,loads是将str转化成dict格式。

dump和load也是类似的功能,只是与文件操作结合起来了。

tips: #类的对象也可以被序列化

def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }
>>> print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}
def dict2student(d):
    return Student(d['name'], d['age'], d['score'])
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>

六、进程和线程

1.多进程
multiprocessing
from multiprocessing import Process
import os

子进程要执行的代码

def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()  #等待子进程结束后再继续往下运行,通常用于进程间的同步
    print('Child process end.')

Pool 进程池

from multiprocessing import Pool
import os, time, random
def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4)   #可以同时跑4个进程,默认大小是CPU的核数
    for i in range(5):  #一共有5个进程
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()  #调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了
    print('All subprocesses done.')

进程间的通信
管道,消息队列,共享内存;

2. 多线程
由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例
import time, threading

#新线程执行的代码:

def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
Lock
多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了
import time, threading
#假定这是你的银行存款:
balance = 0
def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n
#def run_thread(n):
for i in range(2000000):  #同时修改一个变量会出问题
change_it(n)
balance = 0
lock = threading.Lock()
def run_thread(n):
    for i in range(100000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)           

优点
确保了某段关键代码只能由一个线程从头到尾完整地执行;
缺点
阻止了多线程并发执行,包含锁的代码只能以单线程模式执行,效率下降;
线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止;

3. 多线程 VS多进程
    **多进程**:较稳定,可以分配到不同的机器上,但是开销大;

    **多线程**:只能分配到一台机器的多个CPU上,一个线程出错会导致整个进程崩溃;
4. 分布式进程
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值