美文网首页
Python知识

Python知识

作者: wangfp | 来源:发表于2017-09-14 18:35 被阅读0次
    • 命名关键字参数

      用于指定关键字
    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 继续程序运行
    

    • 单元测试&文档测试

      参考教程廖雪峰

    相关文章

      网友评论

          本文标题:Python知识

          本文链接:https://www.haomeiwen.com/subject/lagrsxtx.html