Skip to content

Latest commit

 

History

History
509 lines (382 loc) · 15.6 KB

Magic Python.md

File metadata and controls

509 lines (382 loc) · 15.6 KB

Table of Contents generated with DocToc

Magic Python

树结构

One-line Tree in Python

from collections import defaultdict

# 定义一个可形成树的方法
tree = lambda: defaultdict(tree)

# 实例化一个树
database = tree()
database['users']['name'] = 'ecmadao'
# 如果是普通的字典,则对于不存在的多层嵌套的赋值会直接报错
# 必须先 database['users'] = {}

import json
print(json.dumps(database))
# {"users": {"name": "ecmadao"}}

使用这种方式更容易理解:

database = defaultdict(lambda: defaultdict())
database["users"]["name"] = "ecmadao"

fromkeys

提供默认的值,通过一个list生成一个dict

# ⽤序列做 key,并提供默认value
dict.fromkeys(["a", "b", "c"], 1)
# {'c': 1, 'b': 1, 'a': 1}

拆箱

example = [1, 2, 3, 4]
a, *b, c = example
# a -> 1
# b -> [2, 3]
# c -> 4

反转字典

使用zip

example = {
  'a': 1,
  'b': 2,
  'c': 3
}
tmp = zip(example.values(), example.keys())
# [(1, 'a'), (2, 'b'), (3, 'c')]
result = dict(tmp)
# {1: 'a', 2: 'b', 3: 'c'}

使用字典推导式

example = {
  'a': 1,
  'b': 2,
  'c': 3
}
elpmaxa = {key: value for value, key in example.items()}
# {1: 'a', 2: 'b', 3: 'c'}

合并多个字典

摘自

编程派 -- 怎样合并字典最符合Python语言习惯?

default = {...} # dict 1
info = {...} # dict 2

# method 1
# 手动更新较麻烦
dict_example = default.copy()
dict_example.update(info)

# method 2
# 稍有重复
from itertools import chain
dict_example = dict(chain(default.items(), info.items()))

# method 3
# ChainMap是按照顺序检索字典的,所以info会在default之前返回匹配的值
# 返回值不是字典,而是类似字典的映射
from collections import ChainMap
dict_example = ChainMap({}, info, default)

# method 4
# method 3结果转换为dict
from collections import ChainMap
dict_example = dict(ChainMap({}, info, default))

# method 5
# 最优解
dict_example = {**default, **info}  # python 3.5以后版本支持

命名元组

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])

p = Point(x=11, y=22)
p[0] + p[1] # 33
p.x # 11
p.y # 22
# namedtuple._make(sequence/iterable) 通过序列或生成器来实例化一个命名元组
Point = namedtuple('Point', ['x', 'y'])
p = Point._make([11, 22])

# namedtuple._asdict() 作用于实例化的命名元组,返回一个新的OrderedDict
p._asdict()
# OrderedDict([('x', 11), ('y', 22)])

# namedtuple._replace() 替换命名元组内的某些值并返回新的实例
p._replace(x=33)
# Point(x=33, y=22)

# namedtuple._fields() 返回命名元组中key组成的元组
p._fields()
# ('x', 'y')

命名元组在通过csv包解析csv文件的时候很有用

EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

import csv
for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
    print(emp.name, emp.title)

读写 XML/CSV

文件操作

os

import os

os.listdir(path) # 列出目录下所有文件
os.isfile(path) # 判断是否是文件
os.path.splitext(path) # 把一个文件分为文件名和后缀
os.path.join() # 合并路径
os.path.exists(path) # 检查是否存在
os.makedirs(dir) # 创建文件夹

os.walk(path) # 遍历每个目录将会返回两个列表(一个文件列表,一个目录列表)

High-level file operations

# 复制文件
import shutil

shuilt.copyfile(src, dst)
# 把src复制至dst

beautifulsoup的技巧

使用bs4分析html的时候,可能会遇见DOM解析出错的时候:

target_url = soup.find('a').get('href')
# or
content = soup.find('div', attr={"class": "demo"}).string

如果DOM中没有找到目标元素,还进一步使用get的话,则会报出异常。因此,在没有获取到元素的时候,返回{}就可以避免get出错

target_url = (soup.find('a') or {}).get('href')

关于类

添加__slots__属性,内部指明类的属性名

__slots__ = ['year', 'month', 'day']

定义一个公用的基类,内部定义公用的__init__函数

class Structure:
    _fields = []

    def __init__(self, *args):  # 缺点:参数定义不明确,不支持关键字参数
        if len(args) != len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        # Set the arguments
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

之后继承基类的其他子类可以避免写繁琐的__init__方法

class Stock(Structure):
    _fields = ['name', 'shares', 'price']

属性的代理访问 -- 代理模式

class Person:
	def __init__(self, name):
		self._name = name
	@property
	def name(self):
		return self._name
	@name.setter
	def name(self, value):
		if not isinstance(value, str):
			raise TypeError('Expected a string')
		self._name = value

为什么装饰器返回和设置的是_name而不是name?那是因为这样的话,可以在初始化的时候就触发setter来进行类型的检查。

不要写没有做任何其他额外操作的property。 它会让你的代码变得很臃肿,运行起来变慢很多。

在子类中扩展一个property可能会引起很多不易察觉的问题, 因为一个property其实是 getter、setter 和 deleter 方法的集合,而不是单个方法。 因此,但你扩展一个property的时候,你需要先确定你是否要重新定义所有的方法还是说只修改其中某一个

# 父类
class Person:
    def __init__(self, name):
        self._name = name

    # Getter function
    @property
    def name(self):
        return self._name

    # Setter function
    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._name = value
# 子类
# 只扩展property的getter方法
class SubPerson(Person):
    @Person.name.getter
    def name(self):
        print('Getting name')
        return super().name

# 只扩展setter方法
class SubPerson(Person):
    @Person.name.setter
    def name(self, value):
        print('Setting name to', value)
        super(SubPerson, SubPerson).name.__set__(self, value)

一个描述器就是一个实现了三个核心的属性访问操作(get, set, delete)的类, 分别为 __get__()__set__()__delete__()

# 定义一个描述器
class CheckInteger:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise TypeError('Expected an int')
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        del instance.__dict__[self.name]
# 使用描述器
# 需将这个描述器的实例作为类属性放到一个类的定义中
class Point:
    x = CheckInteger('x')
    y = CheckInteger('y')

    def __init__(self, x, y):
        self.x = x
        self.y = y
# 之后,所有对描述器属性(x或y)的访问会被 __get__() 、__set__() 和 __delete__() 方法捕获到

point = Point(1, 2)
point.x # 调用 Point.x.__get__(point, Point)
point.x = '1' # TypeError('Expected an int')

杂项

从可迭代对象中随机选取元素

import random

foo = (1, 2, 3, 4, 5)
random.choice(foo)

生成包含大写字母和数字的随机字符串

string有方法能让我们方便的获取所有字母和数字

import string
string.ascii_lowercase # abcdefghijklmnopqrstuvwxyz
string.ascii_uppercase # ABCDEFGHIJKLMNOPQRSTUVWXYZ
string.ascii_letters # abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
string.digits # 0123456789

通过random.choice即可进行随机选择

import string
import random

def id_generator(size=6, chars=None):
	chars = chars if chars is not None else string.ascii_letters + string.digits
	return ''.join([random.choice(chars) for _ in range(size)])

id_generator() # 随机生成一个混合大小写和数字的六位码

检验一个字符串是否是数字

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False
# 对字符串对象用isdigit()方法(只能检查是否是整数):
a = "03523"
a.isdigit()
# True
b = "963spam"
b.isdigit()
# False

数据处理

excel/word/xml/csv

注:openpyxl读取文件类型不支持xls但支持xlsx,可以使用xlrd库进行xls的读取,或者直接将xls转换为xlsx

墙裂推荐xlsxwriter

图像

计算

数据库

其他

爬虫

命令行

workflow