Python知识

  • 命名关键字参数

    用于指定关键字
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='')

  1. 在可能引发异常的地方使用print()语句
def foo(s):
    n = int(s)
    print('>>> n = %d' % n)
    # 此处可能引发异常,因此在此前使用print()函数打印出可能原因
    return 10/n

foo('0')
  1. 在可能引发异常的地方使用断言(assert)
def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n

foo('0')
# 使用断言,如断言为True,则跳过;否则,引发AssertionError
# 可以在启动解释器时使用 -O 参数,跳过断言
  1. 使用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;脚本运行后只会显示出指定及之后的信息
  1. 启动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                       # 结束调试
  1. 使用Python的pdb(2)
import pdb

s = '0'
n = int(s)
# 在可能引发异常的地方设置断点
pdb.set_trace()
print(10/n)
# 在正常运行脚本后,脚本会自动在pdb.set_trace()处暂停,并进入调试环境,此时可以使用 p 查看变量,或者 c 继续程序运行

  • 单元测试&文档测试

    参考教程廖雪峰

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • input(str)中可以输入打印字符串 tuple是常量list 单个元素的tuple用(a,)表示,注意逗号 ...
    不掉发码农阅读 1,043评论 0 50
  • 个人笔记,方便自己查阅使用 Py.LangSpec.Contents Refs Built-in Closure ...
    freenik阅读 67,696评论 0 5
  • 内置函数Python解释器内置了许多功能和类型,总是可用的。他们是按字母顺序列在这里。 abs(x)返回一个数的绝...
    uangianlap阅读 1,236评论 0 0
  • python学习笔记 声明:学习笔记主要是根据廖雪峰官方网站python学习学习的,另外根据自己平时的积累进行修正...
    renyangfar阅读 3,040评论 0 10
  • 金钟奏响富强曲, 国人齐唱小康歌。 庆党百岁生曰时, 美景定现百姓窝。
    黄晓红阅读 255评论 1 2