-
命名关键字参数
用于指定关键字
def f(a, *, b, c):
pass
# 参数b, c是命名关键字参数,必须以关键字参数的形式传入(命名关键字参数不可缺省,也不能出现额外的关键字参数)
def f(a, *, b=1, c):
pass
# 命名关键字参数可以指定默认值(此时该命名关键字参数可以缺省)
def f(a, *b, c, d):
pass
# a-位置参数
# b-可变参数
# c-命名关键字参数
参数定义的顺序必须是:位置参数、默认参数(关键字参数)、可变参数、命名关键字参数、可变关键字参数
-
普通函数vs生成器函数
普通函数是顺数执行,遇到return语句或者最后一行函数语句就返回。
生成器函数在每次调用next()函数时执行,遇到yield语句返回;再次执行next()函数是,执行至下一个yield处,直到引发StopIteration异常(python中,右边语句先执行)Iterable(可迭代对象):可用于for循环中的对象;
Iterator(迭代器):可被next()函数调用的对象。
(list、dict、str等是Iterable,但不是Iterator)
-
装饰器函数
保留被装饰函数名称的方法
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('call {}.()'.format(func.__name__)
return func(*args, **kwargs)
return wrapper
-
偏函数
import functools
# 相当于将int()函数的base参数固定为2
int2 = functools.partial(int, base=2)
# 相当于对max()函数增加一个位置参数10
max10 = functools.partial(max, 10)
# partial()函数接口
partial(func, *args, **kwargs)
-
鸭子类型
实现了某种协议,而没有继承相关类型的对象。称之为“类X对象”
-
元类
主要借鉴自廖雪峰教程
# 以ORM为例
class Field(object):
def __init__(self, name, column_type):
self.name = name
self.column_type = column_type
def __str__(self):
return '<{}: {}>'.format(self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super().__init__(name, 'bigint')
class ModelMetaclass(type):
# 主要是对attrs参数的修改
def __new__(cls, name, bases, attrs):
if name == 'Model':
return super().__new__(cls, name, bases, attrs)
# 主要的定义工作是针对Model的子类进行的(子类自动继承父类的元类)
print('Found model: %s' % name)
mappings = {}
# 将Field类从attrs中取出并放在同一个字典中(ORM中的'M')
for key, value in attrs.items():
if isinstance(value, Field):
print('Found mappings: %s ==> %s' % (key, value))
mappings[key] = value
for key in mappings:
# 从attrs中将Field类取出
attrs.pop(key)
# 添加attrs中的映射
attrs['__mappings__'] = mappings
attrs['__table__'] = name
return super().__new__(cls, name, bases, attrs)
# 关键字参数metaclass不是继承的意思,而表示创建自某个元类(元类都是直接继承type类)
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
# 继承dict类,因此以字典的方式处理初始参数
super().__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError('{!r} object has no attribute {!r}'.format(self.__class__.__name__, key)) from None
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
for key, value in self.__mappings__.items():
# value是Field类的实例
# Django中则直接以key作为相应列的名称
fields.append(value.name)
params.append('?')
# key是Field类实例的引用(可能会在Model或者子类实例的创建中被初始化)
args.append(getattr(self, key, None))
sql = 'INSERT INTO {} ({}) VALUES ({})'.format(self.__table__, ', '.join(fields), ','.join(params))
print('SQL: {}'.format(sql))
print('ARGS: {}'.format(args))
# 默认继承父类的元类,因此在编译(?)时,会通过ModelMetaclass类来实现
# 类在导入(或者运行而不被调用)时,解释器会先执行类的属性(包括方法)以构建类。
# 因此OrmTest类中的类属性(Filed实例对象)会在编译时直接被ModelMetaclass元类调用(即保存在attrs并进行相关处理)
# 在程序被作为模块导入(或者运行)时会自动运行ModelMetaclass中相关的输出语句
class OrmTest(Model):
name = StringField('name')
age = IntegerField('age')
email = StringField('Email_test')
需要注意Model类的save()方法定义中
for key, value in self.__mappings__.items():
fields.append(value.name)
params.append('?')
args.append(getattr(self, key, None))
# 在创建Model子类的实例并初始化过程中,SQL语句用到的是OrmTest类中Field子类的实例
# 而相关类属性(Field子类实例)通过自定义元类保存在类的__mappings__属性中,从而可以通过__mappings__属性筛选出需要在SQL语句中使用的数据(根据key值)
使用ORM的目的应该是对实例初始化时数据的检验:
def __setattr__(self, key, value):
if key in self.__mappings__:
column_type = self.__mappings__[key].column_type
if isinstance(value, column_type):
self.key = value
else:
raise TypeError()
else:
self[key] = value
# 不过这种方法无法在实例初试化时就进行检验
# 应该需要定义描述符类来进行检验
描述符对数据类型检验的一个简单实现(主要参考《Fluent Python》)
import re
class Field(object):
__counter = 0
def __init__(self, column_type, blank=False, default=None):
cls = type(self)
prefix = cls.__name__
index = cls.__counter
self.storage_name = '_{}#{}'.format(prefix, index)
cls.__counter += 1
self.column_type = column_type
self.blank = blank
# 应该增加一个对default类型检验的方法
self.default = default
def __get__(self, instance, owner):
return self
def __set__(self, instance, value):
value = self.validate(instance, value)
setattr(instance, self.storage_name, value)
# 在赋值前,对实例的值进行类型检查
def validate(self, instance, value):
var = re.search(r'#(\w+)$', self.storage_name).group(1)
if value:
if isinstance(value, self.column_type):
return value
else:
raise ValueError('{!r} must be {}'.format(var, self.column_type.__name__))
else:
if self.blank:
return self.default
else:
raise ValueError('{!r} cannot be None'.format(var))
class StringField(Field):
def __init__(self, blank=False, default=None):
super(StringField, self).__init__(str, blank=blank, default=default)
class IntegerField(Field):
def __init__(self, blank=False, default=None):
super().__init__(int, blank=blank, default=default)
class ModelMetaclass(type):
# 主要是对attrs参数的修改
def __new__(cls, name, bases, attrs):
if name == 'Model':
return super().__new__(cls, name, bases, attrs)
mappings = []
# 将Field类从类的attrs中取出并放在同一个列表中,以方便之后使用
for key, value in attrs.items():
if isinstance(value, Field):
# 给描述符类中的storage_name起名,使其同实例的属性名称区分开来
value.storage_name = '_{}#{}'.format(value.__class__.__name__, key)
mappings.append(key)
attrs['__mappings__'] = mappings
attrs['__table__'] = name
return super().__new__(cls, name, bases, attrs)
class Model(metaclass=ModelMetaclass):
def __init__(self, **kw):
for k, v in kw.items():
setattr(self, k, v)
# 对未初始化的描述符类属性进行初始化(以检验该属性是否blank==True)
for i in self.__class__.__mappings__:
if i not in kw.keys():
setattr(self, i, None)
def save(self):
fields = []
params = []
args = []
for item in self.__mappings__:
fields.append(item)
params.append('?')
# 此处未能剔除多余的初始变量
args = [value for value in self.__dict__.values()]
# args = [getattr(self, getattr(self, item).storage_name) for item in self.__mappings__]
sql = 'INSERT INTO {} ({}) VALUES ({})'.format(self.__table__, ', '.join(fields), ','.join(params))
print('SQL: {}'.format(sql))
print('ARGS: {}'.format(args))
class OrmTest(Model):
name = StringField()
age = IntegerField(blank=True, default=0)
email = StringField(blank=True, default='')
-
调试方法(参考网站)
- 在可能引发异常的地方使用print()语句
def foo(s):
n = int(s)
print('>>> n = %d' % n)
# 此处可能引发异常,因此在此前使用print()函数打印出可能原因
return 10/n
foo('0')
- 在可能引发异常的地方使用断言(assert)
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
foo('0')
# 使用断言,如断言为True,则跳过;否则,引发AssertionError
# 可以在启动解释器时使用 -O 参数,跳过断言
- 使用logging来记录错误
import logging
logging.basicConfig(level=logging.INFO)
def foo(s):
n = int(s)
logging.info(n = %d' % n)
return 10 / n
foo('0')
# 相当于将第一种方法里面的print()函数都替换成logging
# logging有四个等级:debug,info,warning,error;脚本运行后只会显示出指定及之后的信息
- 启动Python的pdb(python debug?)(1)
# err.py
s = '0'
n = int(s)
print(10/n)
# 启动方式
$ python -m pdb err.py
# pdb命令
(Pdb) l # (字母l)查看源代码
(Pdb) n # 单步执行代码
(Pdb) p <变量名> # 查看变量(但似乎只能查看全局变量)
(Pdb) q # 结束调试
- 使用Python的pdb(2)
import pdb
s = '0'
n = int(s)
# 在可能引发异常的地方设置断点
pdb.set_trace()
print(10/n)
# 在正常运行脚本后,脚本会自动在pdb.set_trace()处暂停,并进入调试环境,此时可以使用 p 查看变量,或者 c 继续程序运行
-
单元测试&文档测试
参考教程廖雪峰
网友评论