第3章 py基础考察点

py基础考察点
py语言特性

  • py是动态强类型语言
  • 动态还是静态指的是编译期还是运行期确定类型
  • 强类型指的是不会发生隐式类型装换

为什么使用py?

* 胶水语言,轮子多,应用广泛
* 语言灵活,生产力高
*  性能问题,代码维护问题,py2/py3不兼容

什么是鸭子类型?

  • 当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子
    关注点 在对象的行为,而不是类型(duck typing)
    eg: file,StringIO,socket对象都支持read/write方法
    (file like object) 再比如定义__iter__魔法方法的对象可以用for迭代 鸭子类型更关注接口而非类型
class Duck:
    def quack(self):
        print("gua gua")

class Person:
    def quack(self):
        print("我是人类, 但我也会guo guo guo")

def in_the_forest(duck):
    duck.quack()

def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)
    print(type(donald))
    print(type(john))
    print(isinstance(donald, Duck))
    print(isinstance(john, Person))

game()

什么是monkey patch? 哪些地方用到了?自己如何实现?

  • 所谓的monkey patch 就是运行时替换
//eg:gevent库需要修改内置的socket

from gevent import monkey; monkey.patch_socket()

import socket
print(socket.socket)

print("After monkey patch")
from gevent import monkey

monkey.patch_socket()
print(socket.socket)

import select
print(select.select)
monkey.patch_select()
print("After monkey patch")
print(select.select)

import time
print(time.time())

def _time():
    return 1234

time.time = _time

print(time.time())

什么是自省?

  • introspection

    运行时判断一个对象的类型的能力,py一切皆对象 用type,id,isinstance获取对象类型信息

    inspect模块提供了更多获取对象信息的函数

    ll = [1, 3, 3]
    d = dict(a=1) #{a:1}
    
    print(type(ll))
    print(type(d))
    
    print(isinstance(ll, list))
    print(isinstance(d, dict))
    
    def add(a, b):
      if isinstance(a, int):
          return a + b
      elif isinstance(a, str):
          return a.upper() + b
    
    print(add(1, 2))
    print(add('head', 'tail'))
    
    print(id(ll))
    print(id(d))
    print(ll is d)
    print(ll is ll)
    

什么是列表和字典推导

List Comprehension

'''[i for i in range(10) if i % 2 == 0]
一种快速生成list/dict/set的方式。用来替代map/filter等
(i for i in range(10) if i % 2 == 0) 返回生成器'''

eg:
    a = ['a', 'b', 'c']
    b = [1, 2, 3]
    d = {}
    for i in range(len(a)):
        d[a[i]] = b[i]
    print(d)

    d = {k: v for k, v in zip(a, b)}
    print(d)

# !< output
    {'a': 1, 'b': 2, 'c': 3}
    {'a': 1, 'b': 2, 'c': 3}

eg:
    l = [i for i in range(10)]
    print(l)
    print(type(l))

    l = (i for i in range(10))

    print(type(l))

    for i in l:
        print(i)

使用生成器可以大大节省内存

py之禅

import this

py2/3差异常考题

  • 使用pyenv安装python版本

py3改进

? print成为函数

? 编码问题,py3不再有unicode对象,默认str就是unicode

? 除法变化,py3除号返回浮点数

? 5/2 = 2.5

? 5//2 = 2
类型注解(type hint) 帮助IDE实现类型检查

def hello(name: str)->str:
    return "hello" + name

print(hello("laowang"))

优化的super()方便直接调用父类函数

高级解包操作, a,b, *ret = range(10)

a, b, *c = range(10)
print(a)
print(b)
print(c)

a, b, *_ = range(10)
print(a)
print(b)
#后面没有数字被舍弃

py3改进
Keyword only arguments 限定关键字参数
eg:
#限定关键字参数需要指定参数名传参


def add(a, b, *, c):
    return a + b +c

print(add(1, 2, c = 3))

Chained exceptions Py3重新抛出异常不会丢失栈信息
#raise from 保留异常栈信息

一切返回迭代器

range, zip, map, dict.values,etc
are all iterators

py3新增
* yield from 链接子生成器

* asyncio内置库, async/await 原生协程支持异步编程

新的内置库enum, mock, asyncio, ipaddress, concurrent.futures等

py3改进

  • 生成的pyc文件统一到__pycache__ 一些内置库的修改, urlib, selector等
  • 性能优化等

一些兼容2/3的工具

? six模块

py2 to py3等工具转换代码

__future__

py函数常考题
常考点
参数传递
(不)可变对象
可变参数

以下代码分别输出什么?

* 可变类型作为参数
def flist(l):
    l.append(0)
    print(l)

l = []
flist(l)
flist(l)

# !< out:
    [0]
    [0, 0]
  • 不可变类型作为参数
def fstr(s):
    s += 'a'
    print(s)

s = "hehe"
fstr(s)
fstr(s)

# !< out:
    "hehea"
    "hehea"

py如何传递参数
一个容易混淆的问题
传递值还是引用呢?都不是。唯一支持的参数传递是共享传参
Call by Object(Call by Object Reference or Call by Sharing)
Call by sharing(共享传参) 函数形参获得实参中各个引用的副本
py一切皆对象

py可变/不可变对象
搞懂可变和不可变内置对象有利于理解函数参数的副作用
哪些是可变和不可变对象?

不可变对象 bool/int/float/tuple/str/frozenset

可变对象 list/set/dict
def clear_list(l):
    l = []

ll = [1, 2, 3]
clear_list(ll)
print(ll)

py可变参数作为默认参数 记住默认参数只计算一次

def flist(l = [1]):
    l.append(l)
    print(l)

fl()
fl()

py *args **kwargs

函数传递中*args, **kwags 含义是?
用来处理可变参数
*args被打包成tuple

? **kwargs被打包成dict

def print_multiple_args(*args):
print(type(args), args)
for idx, val in enumerate(args):
    print(idx, val)

print_multiple_args('a', 'b', 'c')
print_multiple_args(*['a', 'b', 'c'])

# !< out:
    <class 'tuple'> ('a', 'b', 'c')
    0 a
    1 b
    2 c
def print_kwargs(**kwargs):
    print(type(kwargs))
    for k, v in kwargs.items():
        print('{}: {}'.format(k, v))

print_kwargs(a=1, b=2)
print_kwargs(**dict(a=1, b=2))

# !< out:
    <class 'dict'>
    a: 1
    b: 2
def print_all(c, *args, **kwargs):
    print(c)
    if args:
        print(args)
    if kwargs:
        print(kwargs)

print_all(1, "hello", a="muke")

1
('hello',)
{'a': 'muke'}

py异常机制常考题

什么是py的异常?
py使用异常处理错误(有些语言使用错误码)
BaseException

? SystemExit / KeyboardInterrupt / GeneratorExit

? Exception

使用异常的常见场景

? 什么时候需要捕获处理异常?看py内置异常的类型

? 网络请求(超时, 连接错误等)

? 资源访问(权限问题, 资源不存在)

? 代码逻辑(越界访问, KeyError等)

如何处理py异常

搞懂几个关键字

try:
    #func       #可能会抛出异常的代码
except (Exception1, Exception 2) as e: #可以捕获多个异常并处理
    #异常处理的代码
else:
    #pass       #异常没有发生的时候代码逻辑
finally:
    #pass       #无论异常有没有发生都会执行的代码, 一般处理资源的关闭和释放

如何自定义异常

如何自定义自己的异常?为什么需要定义自己的异常?

? 继承Exception实现自定义异常(为什么不是BaseException)
? 给异常加上一些附加信息
? 处理一些业务相关的特定异常(raise MyException)

class MyException(Exception):
    pass

try:
    raise MyException('my exception')
except MyException as e:
    print(e)    

py性能分析与优化,GIL常考题

什么是Cpython GIL
GIL, Global Interpreter Lock

  • Cpython解释器的内存管理并不是线程安全的
  • 保护多线程情况下对python对象的访问
  • Cpython使用简单的锁机制避免多个线程同时执行字节码

GIL的影响
限制程序的多核执行
同一个时间只能有一个线程执行字节码
CPU密集程序难以利用多核优势
IO期间会释放GIL, 对IO密集程序影响不大
如何规避GIL影响
区分CPU和IO密集程序
CPU密集可以使用多进程+进程池
IO密集使用多线程/协程
cpython扩展
GIL的实现

# 请问这段代码输出?
import threading

n = [0]

def foo():
    n[0] = n[0] + 1
    n[0] = n[0] + 1

threads = []
for i in range(5000):
    t = threading.Thread(target=foo)
    threads.append(t)

for t in threads:
    t.start()

print(n)
# !< 加锁操作
import threading
lock = threading.lock()
n = [0]

def foo():
    with lock:
        n[0] = n[0] + 1
        n[0] = n[0] + 1

threads = []
for i in range(5000):
    t = threading.Thread(target=foo)
    threads.append(t)

for t in threads:
    t.start()

print(n)

为什么有了GIL还要关注线程安全?
py中什么操作才是原子的?

? 一步到位执行完
? 一个操作如果是一个字节码指令可以完成就是原子的
? 原子的是可以保证线程安全的
? 使用dis操作来分析字节码

为什么有GIL还要关注线程安全?

#原子操作
import dis

def update_list(l):
    l[0] = 1 #原子操作, 不用担心线程安全问题

#dis.dis(update_list)
"""
280           0 LOAD_CONST               1 (1)
              2 LOAD_FAST                0 (l)
              4 LOAD_CONST               2 (0)
              6 STORE_SUBSCR        #单字节码操作,线程安全
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
"""
#非原子操作 不是线程安全
def incr_list(l):
    l[0] += 1 #危险!!不是原子操作

dis.dis(incr_list)
"""
295           0 LOAD_FAST                0 (l)
              2 LOAD_CONST               1 (0)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_CONST               2 (1)
             10 INPLACE_ADD        #需要多个字节码操作, 有可能在线程执行
                                   #过程中切到其它线程
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
"""

如何剖析程序性能
使用各种profile工具(内置或第三方)

  • 二八定律,大部分时间耗时在少量代码上
  • 内置的profile/cprofile等工具
  • 使用pyflame(uber开源)的火焰图工具

服务端性能优化措施
web应用一般语 言不会成为瓶颈

  • 数据结构与算法优化
  • 数据库层:索引优化 慢查询消除 批量操作减少IO,NoSQL
  • 网络IO:批量操作, pipeline操作 减少IO
  • 缓存:使用内存数据库 redis/memcached
  • 异步:asyncio celery
  • 并发:gevent/多线程

py生成器与协程
Generator

  • 生成器就是生成值得函数
  • 当一个函数有了yield关键字就成了生成器
  • 生成器可以挂起执行并且保持当前执行的状态

什么是生成器

def simple_gen():
    yield 'hello'
    yield 'world'

gen = simple_gen()
print(type(gen))    #'generator' object
print(next(gen))    #'hello'
print(next(gen))    #'world'

基于生成器的协程

? py3之前没有原生协程,只有基于生成器的协程
pep342(Coroutines via Enhanced Generators)增强生成器功能

生成器可以通过yield暂停执行和产出数据

? 同时支持send()向生成器发送数据和throw()向生成器抛异常

# !< Generator Based Coroutine
def coro():
hello = yield 'hello' #yield关键字在右边作为表达式, 可以被send值
yield hello

c = coro()
#输出'hello' 这里调用next产出第一个值'hello', 之后函数暂停
print(next(c))
#再次调用send发送值 此时hello变量赋值为'world' 之后yield产出hello变量的值'world'

print(c.send('world'))
#之后协程结束, 后续再send值会抛异常StopIteration

协程注意点
协程需要使用send(None)或者next(coroutine)来预激prime才能启动
在yield处协程会暂停执行
单独的yield value 会产出值给调用方
可以通过coroutine.send(value)来给协程发送值,发送的值会赋值给yield表达式左边的变量
value = yield
协程执行完成后(没有遇到下一个yield语句)会抛出StopIteration异常

协程装饰器
避免每次都要用send预激它

from functools import wraps

def coroutine(func): #不用每次都用send(None)启动
    """装饰器:向前执行到第一个'yield'表达式,预激`func`"""
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return primer

py3原生协程
py3.5引入async/await支持原生协程(native coroutine)

import asyncio
import datetime
import random

async def display_date(num, loop):
    end_time = loop.time() + 50.0
    while True:
        print('Loop: {} Time: {}'.format(num, datetime.datetime.now()))
        if(loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(random.randint(0, 5))

loop = asyncio.get_event_loop()
asyncio.ensure_future(display_date(1, loop))
asyncio.ensure_future(display_date(2, loop))
loop.run_forever()

单元测试
什么是单元测试
Unit Testing
针对程序模块进行正确性检验
一个函数, 一个类进行验证
自底向上保证程序正确性

为什么要写单元测试
三无代码不可取(无文档, 无注册, 无单测)

  • 保证代码逻辑的正确性(甚至有些采用测试驱动开发(TDD))
  • 单测影响设计, 易测的代码往往是高内聚低耦合的
  • 回归测试, 防止改一处整个服务不可用

单元测试相关的库

  • nose/pytest 较为常用
  • mock模块用来模拟替换网络请求等
  • coverage统计测试覆盖率
def binary_search(array, target):
    if not array:
        return -1
    beg, end = 0, len(array)
    while beg < end:
        mid = beg + (end - beg) // 2 #py3
        if array[mid] == target:
            return mid
        elif array[mid] > target:
            end = mid
        else:
            beg = mid + 1
    return -1

def test():
    """
    如何设计测试用例:等价类划分
    - 正常值功能测试
    - 边界值(eg 最大最小, 最左最右值)
    - 异常值(eg None, 空值, 非法值)
    :return:
    """
    #正常值, 包含有和无两种结果
    assert binary_search([0, 1, 2, 3, 4, 5], 1) == 1
    assert binary_search([0, 1, 2, 3, 4, 5], 6) == -1
    assert binary_search([0, 1, 2, 3, 4, 5], -1) == -1

    #边界值
    assert binary_search([0, 1, 2, 3, 4, 5], 0) == 0
    assert binary_search([0, 1, 2, 3, 4, 5], 5) == 5
    assert binary_search([0], 0) == 0

    # #异常值
    assert binary_search([], 1) == -1

    #pip install pytest
    #pytest xx.py

py深拷贝和浅拷贝
深拷贝与浅拷贝的区别
什么是深拷贝?什么是浅拷贝?
py中如何实现深拷贝?

原文地址:https://www.cnblogs.com/xuzhaoping/p/11616737.html

时间: 2024-10-09 01:17:02

第3章 py基础考察点的相关文章

第3章 图像基础

  第3章图像基础 在开始构建图像分类器之前,首先要理解图像是什么.首先要理解图像的基石--像素. 1          像素--图像的基石 像素是图像最原始的构建块.每副图像由像素集合构成.通常,像素考虑为给定一副图片下,在给定位置的"颜色"或亮度的"强度".如图1所示: 图1 1000像素宽,750像素高的图像 图1的图像为1000*750分辨率,意味着1000像素宽且750像素高.我们可以用矩阵来概念化图像,在这个例子中,矩阵1000列(宽)和750行(高),

第10章神经网络基础

第10章神经网络基础 在本章中,我们将深入研究神经网络(neural networks)的基础.我们将首先讨论人工神经网络,以及它们是如何从我们自己身体中的真实生物神经网络中得到启发的.之后,我们将回顾经典的感知器算法(Perceptron algorithm)以及它在神经网络历史中的作用. 构建了感知器,我们将学习BP算法(backpropagation algorithm),这是现代神经网络的基石.我们将使用python从头实现BP算法,确保我们理解了这个重要的算法. 当然,现代神经网络库如

萌新向Python数据分析及数据挖掘 第一章 Python基础 第八节 函数

第一章 Python基础 第八节 函数 定义函数 函数 其实就可以理解为外挂,把一些常用的.重复率比较多你又不想重复写的东西写进函数,加上开关实现简化操作 举个简单的例子 1 def greet_user(username): 2 #定义一个叫做"迎接用户"的外挂,让他能直接打印一个问候语,括号里面是函数需要输入的东西,也就是个性化的东西 3 """先是简单的问候语""" 4 print("Hello! "

萌新向Python数据分析及数据挖掘 第一章 Python基础 第九节 类

第一章 Python基础 第九节 类 面向对象编程时,都会遇到一个概念,类,python也有这个概念,下面我们通过代码来深入了解下. 其实类 和函数都是为了节省代码,有了类的的概念,就可以把相同的代码写在父类,子类继承后就可以直接使用,而且通过选择对应的父类就可以直接使用对应父类的内容. 创建和使用类 1 class Dog(): #认识狗这类东西 2 def __init__(self, name, age): #狗是肯定有名字和年龄的,是个狗就有,要用户填写 3 self.name = na

第04章-VTK基础(7)

[译者:这个系列教程是以Kitware公司出版的<VTK User's Guide -11th edition>一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),由于时间关系,我们不能保证每周都能更新本书内容,但尽量做到一周更新一篇到两篇内容.敬请期待^_^.欢迎转载,另请转载时注明本文出处,谢谢合作!同时,由于译者水平有限,出错之处在所难免,欢迎指出订正!] [本小节内容对应原书的第83页至第87页] 4.16 动画 动画是可视化等系统的重要模块: 通过

第04章-VTK基础(5)

[译者:这个系列教程是以Kitware公司出版的<VTK User's Guide -11th edition>一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),由于时间关系,我们不能保证每周都能更新本书内容,但尽量做到一周更新一篇到两篇内容.敬请期待^_^.欢迎转载,另请转载时注明本文出处,谢谢合作!同时,由于译者水平有限,出错之处在所难免,欢迎指出订正!] [本小节内容对应原书的第63页至第70页] 4.11 文本标注 VTK提供了两种方法用于标注图像

第04章-VTK基础(4)

[译者:这个系列教程是以Kitware公司出版的<VTK User's Guide -11th edition>一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),由于时间关系,我们不能保证每周都能更新本书内容,但尽量做到一周更新一篇到两篇内容.敬请期待^_^.欢迎转载,另请转载时注明本文出处,谢谢合作!同时,由于译者水平有限,出错之处在所难免,欢迎指出订正!] [本小节内容对应原书的第52页至第63页] 4.6 控制3D Props VTK中的渲染窗口渲染

第04章-VTK基础(3)

ExecutorService 建立多线程线程池的步骤: 线程池的作用: 线程池作用就是限制系统中执行线程的数量. 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线程排队等候.一个任务执行完毕,再从队列的中取最前面的任务开始执行.若队列中没有等待进程,线程池的这一资源处于等待.当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了:否则进入等待队列. 为什么要用线程池: 1.减少了创建和

第二章 算法基础 思考题2-1

package chap02; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Random; import org.junit.Test; /*** * 在归并排序中对小数组采用插入排序 * * @author xiaojintao * */ public class ques2_1 { /** * 归并排序算法 * * @param a * @return */ static void m