python笔记

列表生成器

使用列表生成器可以简便的产生列表,省去for
注意,python3 对其中num变量视作局部变量,而python2则不是,这会影响其上下文的同名变量值!!!
列表生成器和列表推导几乎替代了map reduce filter等高阶函数的绝大多数功能,一般不再使用高阶函数和匿名函数

l = [num*2 for num in range(1, 5)]
print(l)
#[2, 4, 6, 8]
la = [1, 2]
lb = [4, 5, 6]
l = [Point(a, b) for a in la for b in lb]
#[Point(x=1, y=4), Point(x=1, y=5), Point(x=1, y=6), Point(x=2, y=4), Point(x=2, y=5), Point(x=2, y=6)]

生成器generator

语法与列表生成器类似,仅改为()。区别在于不一次性计算出列表,二是返回一个可迭代对象generator object,这是可迭代的,可以直接送入max,sum等函数

def a(i):
    return i*2
def b(i):
    return i*3
l = [a, b]
print(list(each(10) for each in l))#[20,30]
print(max(each(10) for each in l))#30

列表推导

对于过滤等操作很方便

symbols = '$¢£¥€¤'
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
#[162, 163, 165, 8364, 164]
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
#[162, 163, 165, 8364, 164]

运算符类与map_reduce

尽管列表推导,生成器代替了绝大多数map_reduce应用,仍有些时候会用到。如计算hash值时需要将一系列值异或起来,如下。
其中operator类提供了所有运算符,避免写lambda函数

import functools
import operator
def hash_all(h):
	hashes = (hash(x) for x in h) # 这里做一个可迭代的生成器
	return functools.reduce(operator.xor, hashes, 0)
	#调用运算符类和reduce算法计算h中所有元素相异或的值

namedtuple命名元组

用于方便的构建仅有几个属性的类,生成的namedtuple可以像类一样引用其属性,如下namedtuple用于表示平面点坐标

import collections
Point = collections.namedtuple('Point', ['x', 'y'])
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1.x)

元组

t=(a,b)
first,second=t#拆包
print(t[0])
a, b, *rest = range(5)#平行赋值,多余的存成列表赋给rest
#嵌套的
a=(1,2,(1,2))
q,w,e=a
#q=1 w=2 e=(1,2)

切片

切片的下标逻辑如下,在索引前切开
python笔记
还可以用如下的形式对 s 在 a 和 b之间以 c 为间隔取值。c 的值还可以为负
python笔记
借助切片可方便的赋值
python笔记

排序与插入

fruits = ['grape', 'raspberry', 'apple', 'banana']
#一般排序,原列表并没有变化,返回排好的
print(sorted(fruits))
#传入排序依据函数
print(sorted(fruits, key=len))
fruits.sort()#就地排序


import bisect
import random
random.seed(1729)#输入随机种子
my_list = [random.randrange(1, 10) for each in range(10)]#生成0-9随机类列表
print(my_list)
my_list.sort()#就地排序
bisect.insort(my_list, 3)#按顺序插入(二分)
print(my_list)

数组

接近c数组,可降低list开销

from array import array
from random import random
floats = array('d', (random() for i in range(100)))#‘d’用于指出存储类型
fp = open('floats.bin', 'wb')
floats.tofile(fp)#可输出为二进制
fp.close()
floats2 = array('d')
fp = open('floats.bin', 'rb')
floats2.fromfile(fp, 100)#可从二进制输入
print(floats[0])#可如列表一样引用

DICT

a = ['a', 'b', 'c']
b = [1, 2, 3]
d = zip(a, b)#返回zip对象,可迭代出(a,b)
dic = dict(d)
print(dic['a'])
dicc=dic


#只读DICT
from types import MappingProxyType
d = {1:'A'}
d_proxy=MappingProxyType(d)
print(d_proxy[1])#只读,不可修改
d[2]='B'
#之后d_proxy会同步更新

SET

set是无重复集。与数学上的集合相似,可进行集合运算

a = [1, 2, 3, 3]
b = [2, 3, 4]
sa = set(a)
sb = set(b)
print(sa | sb)
print(sa-sb)
print(sa & sb)
print(sa ^ sb)#求差集
#另有子集,包含等判断

函数

这是一个例子,其中
第一个参数后面的任意个参数会被 *content 捕获,存入一个元组
tag 函数签名中没有明确指定名称的关键字参数会被 **attrs 捕
获,存入一个字典。
cls 参数只能作为关键字参数传入。

def tag(name, *content, cls=None, **attrs):
	"""生成一个或多个HTML标签"""
	if cls is not None:
		attrs['class'] = cls
	if attrs:
		attr_str = ''.join(' %s="%s"' % (attr, value)
		for attr, value
		in sorted(attrs.items()))
	else:
		attr_str = ''
	if content:
		return '\n'.join('<%s%s>%s</%s>' %
		(name, attr_str, c, name) for c in content)
	else:
		return '<%s%s />' % (name, attr_str)

函数内省

在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。即当你拿到一个“函数对象”的时候,你可以继续知道,它的名字,参数定义状况等信息。python关于参数信息存放在函数的特殊属性中,时实际不方便使用,借助inspect包可很好处理
内省常用于检查传入的函数是否符合要求

from inspect import signature


def myfun(a, b=1, *c):
    pass


sig = signature(myfun)
print(sig)
for name, param in sig.parameters.items():
    print(param.kind, ':', name, '=', param.default)

其中kind有五种
POSITIONAL_OR_KEYWORD
  可以通过定位参数和关键字参数传入的形参(多数 Python 函数的参
数属于此类)。
VAR_POSITIONAL
  定位参数元组。
VAR_KEYWORD
  关键字参数字典。
KEYWORD_ONLY
  仅限关键字参数(Python 3 新增)。
POSITIONAL_ONLY
  仅限定位参数;目前,Python 声明函数的句法不支持,但是有些使
用 C 语言实现且不接受关键字参数的函数(如 divmod)支持。

函数注解

python3重要的新特性,用于为函数声明中的参数和返回值附加元数据

def clip(text:str, max_len:'int > 0'=80) -> str:"""在max_len前面或后面的第一个空格处截断文本
	"""
	end = None
	if len(text) > max_len:
		space_before = text.rfind(' ', 0, max_len)
	if space_before >= 0:
		end = space_before
	else:
		space_after = text.rfind(' ', max_len)
	if space_after >= 0:
		end = space_after
	if end is None: # 没找到空格
		end = len(text)
	return text[:end].rstrip()

函数声明中的各个参数可以在 : 之后增加注解表达式。如果参数有默认值,注解放在参数名和 = 号之间。如果想注解返回值,在 ) 和函数声明末尾的 : 之间添加 -> 和一个表达式。那个表达式可以是任何类型。注解中最常用的类型是类(如 str 或 int)和字符串(如 ‘int >0’)。在示例 5-19 中,max_len 参数的注解用的是字符串。python 对注解所做的唯一的事情是,把它们存储在函数的__annotations__ 属性里
print(signature(f).parameters.values())
可调出属性

ABC抽象方法

python本身不提供抽象方法,可用ABC类来辅助检查

from abc import ABC, abstractmethod


class Base(ABC):

    @abstractmethod
    def load(self, input):
        """Retrieve data from the input source and return an object."""
        return


class A(Base):
    def load(self, input):
        print(input)


class B(Base):
    pass


a = A()
b = B()#err

变量作用域&闭包

Python 编译函数的定义体时,默认吧内部变量看做局部变量,若实则指全局,需要显示的声明 global xxx

闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量,如

def make_averager():
	series = []
	def averager(new_value):
		series.append(new_value)
		total = sum(series)
		return total/len(series)
	return averager

这个函数返回avr函数,其中使用了series存储历史值
当make_averager返回时,它会保留定义函数时存在的*变量的绑定(此处的series),这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定!

通过使用nonlocal关键字,可不用列表实现上述函数(这样可正确闭包,否则解释器会将count,total视作局部变量,不加以闭包绑定)

def make_averager():
	count = 0
	total = 0
	def averager(new_value):
		nonlocal count, total
		count += 1
		total += new_value
		return total / count
	return averager

装饰器

装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二个特性是,装饰器在加载模块时立即执行。(函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行)
装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。
比如可借助装饰器帮助注册函数到列表
叠放:越靠近函数的先起装饰作用

fun_list = []


def isfun(fun):
    fun_list.append(fun)
    return fun


@isfun
def a(i):
    return i*2


@isfun
def b(i):
    return i*3


print(fun_list)
#[<function a at 0x0000000000C2EEA0>, <function b at 0x0000000000C2EF28>]

为更好的处理传入函数的参数和特殊属性等,最佳实践是利用functools 中wraps来辅助构建,如下的例子实现了执行时间测试功能,注意传参的处理

import time
from functools import wraps


def clock(func):
    @wraps(func)
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        name = func.__name__
        arg_lst = []
        if args:
            arg_lst.append(', '.join(repr(arg) for arg in args))
        if kwargs:
            pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
            arg_lst.append(', '.join(pairs))
        arg_str = ', '.join(arg_lst)
        print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
        return result
    return clocked


@clock
def fun(a, b, **c):
    print('a=', a, 'b=', b)
    print(c)


fun(1, 2, x=1, y=2)
print(fun.__name__)  # 不改变名称

标准库内置了几个很有用的装饰器
1.
import functools
@functools.lru_cache()
作用是用缓存优化递归算法(通过保存调用结果),此外lru_cache 在从 Web 中获取信息的应用中也能发挥巨大作用。
可接受参数
functools.lru_cache(maxsize=128, typed=False)
maxsize 参数指定存储多少个调用的结果
typed 参数如果设为 True,把不同参数类型得到的结果分开保存
被 lru_cache 装饰的函数会有 cache_clear 和 cache_info 两个方法,分别用于清除缓存和查看缓存信息。
2
@classmethod
用于将方法变成java中静态方法的概念,即不必生成实例即可调用

    @classmethod
    def pargs(self, *args):
        print(*args)

python思维-鸭子类型

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在python的思维中,我们并不关心对象是什么类型,到底是不是鸭子,只关心行为,这一点是和java完全不同的。如下的例子,可见所谓鸭子类型对多态特性的轻巧实现。同样的python提供的大量特殊方法(magic method)也应用了这一思想,如任何实现了 __iter__ 和 __next__方法的对象都可称之为迭代器,而任何类只要实现__getitem__方法,那python的解释器就会把它当做一个collection

class Duck:
    def quack(self):
        print "Quaaaaaack!"
 
class Bird:
    def quack(self):
        print "bird imitate duck."
 
class Doge:
    def quack(self):
        print "doge imitate duck."
 
def in_the_forest(duck):
    duck.quack()
 
duck = Duck()
bird = Bird()
doge = Doge()
for x in [duck, bird, doge]:
    in_the_forest(x)

对象的一些问题

首先一些概念声明
==与is
== 运算符比较两个对象的值(对象中保存的数据),而 is 比较对象的
标识。可以在自己的类中定义 __eq__ 方法,决定 == 如何比较实例。如果不覆盖 __eq__ 方法,那么从 object 继承的方法比较对象的 ID
浅复制
如下,只复制浅层,子列表仍指向相同的内存

l1 = [1, 2, [1, 2]]
l2 = list(l1)
print(id(l1) == id(l2))#flase
print(id(l1[-1]) == id(l2[-1]))#true

若要深复制,使用copy包的deepcopy函数
传参
python传参全部为传引用
这会导致一个不易察觉的问题,即在类中函数用可变对象作为函数的默认参数时,可能会导致多个实例引用到同一个内存上,导致错误。

class常用特殊方法

特殊方法的使用可使类更符合python的思维,便于接入python的str,sum,==迭代,散列等优秀特性。如下面的类

class Test:
    a = 0
    b = None

    def __init__(self, a, b=None):
        self.a = a
        self.b = b
    # 用于被str()方法调用

    def __str__(self):
        return '__str__ method'
    # 调试信息

    def __repr__(self):
        return '__repr__ method'
    # 用于==运算符

    def __eq__(self, other):
        #print(self.a, other.a)
        return self.a == other.a
    # 定义散列函数,使得可用set

    def __hash__(self):
        return hash(self.a)
    # 迭代。这里返回b的生成器

    def __iter__(self):
        return (each for each in self.b)
    # 使得实例可用len()

    def __len__(self):
        return len(self.b) if self.b is not None else 0
    # 这是声明了静态方法

    @classmethod
    def pargs(self, *args):
        print(*args)

另外实现如下的两个特殊方法可实现序列属性,可以实现切片和迭代特性,并可实现[] 访问

class Test:
    _components = []

    def __init__(self, l):
        self._components = l

    def __len__(self):
        return len(self._components)

    def __getitem__(self, index):
        return self._components[index]


t = Test([1, 2, 3])
print(t[0])
for each in t:
    print(each)

全部特殊方法:
流畅的python.pdf-p57

异常

通过捕获异常可以避免脚本直接退出
典型异常处理如下

try:
    fh = open("testfile", "w")
    fh.write("hello world!!")
except IOError:
	#捕获IOError错误
    print("Error: open fail")
else:
	#不发生异常时执行,和try是一对
    print("success!")
    fh.close()

python定义好的标准异常有如下这些
http://www.runoob.com/python/python-exceptions.html
可如下发起异常

raise Exception("Invalid level!")

或raise其他标准异常,通常不必自定义异常类

封包与import

from…import 与 import
区别在于import的要用 模块名.函数名 来调用,而前者直接用函数名即可。import机制会自动处理重复引用问题,无需多虑

包就是文件夹,只要该文件夹下存在 __init__.py 文件。
http://www.runoob.com/python/python-modules.html
一般简单的多文件项目不必使用包,直接import 文件名即可引入

单元测试

使用内置unittest进行
需要import unittest并在测试文件里创建一个测试类。测试文件采用 被测文件名_test.py命名。测试类继承unittest.TestCase来获得测试方法。测试类的测试函数要以test_t开头。
有如下常用的测试函数

#测试值相等
self.assertEqual(add(0, 0), 0)
#测试是否正确抛出异常
with self.assertRaises(AttributeError):
    value = d.empty

测试例子如下:

#temp.py
def add(a, b):
    return a+b
def sub(a, b):
    return a-b

#temp_test.py
import unittest
from temp import add, sub
class TestTest(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(3, 3), 6)
        self.assertEqual(add(4, 3.1), 7.1)
        # self.assertTrue()

    def test_sub(self):
        self.assertEqual(sub(2, 1), 1)

在vscode中,会自动识别出测试类,下方信息条展示测试情况
python笔记
python笔记
python笔记