python的一些高级特性

列表生成式和列表生成器

列表生成式

[x * x for x in range(1, 11) if x % 2 == 0] # [4, 16, 36, 64, 100]
[x * x if x % 2 == 0 for x in range(1, 11)] # 语法错误,if放在前面的时候,必须要有else
[x * x if x % 2 == 0 else 0 for x in range(1, 11)] # [0, 4, 0, 16, 0, 36, 0, 64, 0, 100]

列表生成器

PythonCookbook里面说,列表生成器比生成式的速度要快

(x * x for x in range(10)) # <generator object <genexpr> at 0x1022ef630> 这是一个生成器,可以通过next或者for循环去输出元素
g = (x * x for x in range(10))
for n in g:
	print(n, end=' ') # 0 1 4 9 16 25 36 49 64 81 

迭代器和生成器

生成器

对yield关键字我没有详细解释,想深入理解可以看这篇博客python中yield的用法详解——最简单,最清晰的解释

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b # 当函数运行到这里的时候会停下来,直到调用next的时候,才会往下走,个人感觉有点类似调试
        # test = yield b  这个赋值语句有两步 1.执行yield b 2. test = yield b    
        # yield语句会让程序停止,所以在执行完yield b时,程序卡住 赋值并没有进行完成
        a, b = b, a + b
        n = n + 1
    return 'done'

迭代器

for x in [1, 2, 3, 4, 5]:
    pass

"""
---------------------------------------------
二者是等价的
----------------------------------------------
"""

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

函数式编程

高级函数

map

map映射函数,简单的说就是对每一个元素进行修改。

list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9']

reduce

这是对整个列表进行处理,然后一个结果

from functools import reduce
def add(x, y):
     return x + y
reduce(add, [1, 3, 5, 7, 9]) # 25 

filter

也是对每一个元素进行运算,但是根据返回值是否为True来决定是否舍弃该元素

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # [1, 5, 9, 15]

sorted

可以对列表进行排序

sorted([36, 5, -12, 9, -21], key=abs) # [5, 9, -12, -21, 36]

闭包

所谓闭包就是在外面引用该函数,导致该函数一直存在,,如果要赋初值的时候,需要手动声明这不是局部变量

def createCounter():
    x = 0
    def counter():
        nonlocal x
        x += 1
        return x
    return counter
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5

lambda表达式(匿名函数)

python中万物皆对象,lambda表达式也可以赋值给其他的变量

f = lambda x: x * x
f # <function <lambda> at 0x101c6ef28>
f(5) # 25

装饰器

两层的装饰器

def log(func):
    @functools.wraps(func) # 让now.__name__ = 'now'  如果不加这一句,那么now.__name__ = 'wrapper'
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper


@log           # now=log(now)
def now():
    print('2015-3-25')
    
now() 
# call now():
# 2015-3-25  

三层的装饰器

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')  # now = log('execute')(now)
def now():
    print('2015-3-25')
    
now()
# execute now():
# 2015-3-25
import functools


def log(func):
    if callable(func):  # 判断该参数是否是函数,如果是函数那么只需要两层装饰器,如果不是函数,那么需要三层装饰器
        @functools.wraps(func)
        def wrap(*args, **kw):
            print("begin call")
            r = func(*args, **kw)
            print("end call")
            return r

        return wrap

    text = func
    def decorator(func):
        @functools.wraps(func)
        def wrap(*args, **kw):
            print(text)
            print("begin call")
            r = func(*args, **kw)
            print("end call")
            return r

        return wrap

    return decorator


@log
def f():
    pass


@log('111')
def g():
    pass
# 实现了装饰器,可以带参数,也可以不带参数。重点是callable函数!!!

f()
print("--------------------")
g()

对象

多态特性

python是鸭子类型,并不是说一定要是子类才行,只要是存在该方法,都会完美运行

def run_twice(animal):
    animal.run()
    animal.run()

isinstance() 与 type() 区别:

  • type() 不会认为子类是一种父类类型,不考虑继承关系。
  • isinstance() 会认为子类是一种父类类型,考虑继承关系。
print(type(dog) == Animal)    #False
print(type(dog) == Dog)    #True
print(isinstance(dog, Animal))   #True
print(isinstance(dog, Dog))    #True

类变量和实例变量

定义在最开始并且在init之外的变量为类变量,可以通过类名访问,也可以通过实例对象访问,并且所有对象的类变量都是相同的。但是对于实例变量,只能通过实例对象访问。

class Student(object):
    name = 'Student' # 类变量
    
    def __init__(self, name):
        self.name = name # 实例变量

setattr和getattr

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
s = Student()
s.name # 'Michael'

s.score # 99     

__slots__

因为python是动态语言,每个实例对象都可以随意的创建属性和方法,但是我们又不想太随意,可以通过__slots__来限制他可以创建的属性和方法

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    
   
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定失败,不允许创建该属性

@property

可以把get方法变成一个属性来调用

class Student(object):

    @property  # 可以通过self.score直接调用
    def score(self):
        return self._score
"""
	@property
    def score(self):
        return self.score
    结果会是无限递归,最终导致栈溢出。 因为@property会把方法变成属性
"""   
    @score.setter # 如果没有设置setter 那么该属性只能读,不能写
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

多重继承

# 大类:
class Mammal(Animal):
    pass

class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')
        


# 这种写法虽然有多继承的关系,但是确实不容易理解,该类主要是从哪个类继承下来的
# 多继承中第一个继承类为主类
class Bat(Mammal, Flyable):
    pass      
   
class Dog(Mammal, Runnable):
    pass


# MixIn原则,即如果不是主要类,那么就遵循MixIn原则。这样就可以明确的看出哪个类是主类,其实感觉有点像Java的继承和接口
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

str,iter,next,call,getitem

str

class Student(object):
	def __init__(self, name):
   		self.name = name
    def __str__(self):
    	return 'Student object (name: %s)' % self.name
    
print(Student('Michael')) # Student object (name: Michael)

iter和next

该方法主要是用于for … in循环调用

# 计算斐波拉契
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1  # 初始化值a,b

    def __iter__(self):
        return self  # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > 100000:  # 退出循环的条件
            raise StopIteration()
        return self.a  # 返回下一个值


for n in Fib():
    print(n, end=' ')  # 1 1 2 3 5 8 13 21 34 55 89 144 233 37

call

该方法在pytorch里面网络部分用的很多,每次forward都调用了该函数.当把实例对象当作函数调用时,会调用call方法

class Foo:

    def __new__(cls, *args, **kwargs):
        print('new方法被调用了')
        return super().__new__( cls)

    def __init__(self, name):
        print('init方法被调用了')

    def __call__(self, *args, **kwargs):
        print('call方法被调用了')


a = Foo("张三")
a() # 这里会调用call方法

getitem

该方法在pytorch里面的数据集部分是必写的,其实就相当于列表切片的功能

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
        
        
        
        
f = Fib()
f[1] # 1
f[0:5] #[1, 1, 2, 3, 5]
f[:10] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

枚举类

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 默认是从1开始的


from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
    
    # 我们可以自己去设定规则,这个装饰器的作用是使得我们定义的变量是唯一的
"""
@unique
class Weekday(Enum):
    Sun = 0  
    Mon = 0
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
    如果这样,会报错,Sun和Mon冲突


@unique
class Weekday(Enum):
    Sun = 1
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
    # 这样也会报错
"""

元类

元类,最重要的是需要理解所有的类都是通过type函数创造的,并且还要理解__new__方法。简单来说就是所有类都是type函数的创造的实例

# 使用type创建类,需要三个参数
# type(类名,继承关系(使用元组表示), 类的一些属性和方法(字典表示)
class Foo:
    pass

print(Foo)  # <class '__main__.Foo'>
print(type('Foo', (object,), {})) # <class '__main__.Foo'>
# 可以看出两者是等价的

这是从廖雪峰老师哪里摘抄的例子

class Field(object):

    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (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(IntegerField, self).__init__(name, 'bigint')


class ModelMetaclass(type):

    # 该类的主要作用是把Filed字段筛选出来

    # type创建类的时候,需要三个参数(类名, 父类,类的一些方法和属性通过字典的形式传递),
    # 所以在使用class关键字创建的时候会默认的调用该方法进行传参,类名作为第一个参数,继承关系作为第二个参数,定义的属性和方法作为第三个参数
    # new方法第一个参数为类对象, 第二参数为类名,后面的参数则是init方法里面接收到的参数
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            print(f'model cls={cls} name={name}, bases={bases}, attrs={attrs}')
            return type.__new__(cls, name, bases, attrs)
        print(f'cls={cls} name={name}, bases={bases}, attrs={attrs}')
        print('Found model: %s' % name)
        mappings = dict()
        # 把所有属于Filed的子类都保存起来
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))


class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

    def hh(self):
        pass


# 创建一个实例:到ModelMetaclass的时候,相当于穿了一个字典{‘id;12345, 'name':'Michael, 'email':test@orm.org', 'password':'my-pwd'}
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()

输出结果


model cls=<class '__main__.ModelMetaclass'> name=Model, bases=(<class 'dict'>,), attrs={'__module__': '__main__', '__qualname__': 'Model', '__init__': <function Model.__init__ at 0x0000020AAB1EC790>, '__getattr__': <function Model.__getattr__ at 0x0000020AAB1ECB80>, '__setattr__': <function Model.__setattr__ at 0x0000020AAB2DFE50>, 'save': <function Model.save at 0x0000020AAB2DF9D0>, '__classcell__': <cell at 0x0000020AAB2D9B50: empty>}
cls=<class '__main__.ModelMetaclass'> name=User, bases=(<class '__main__.Model'>,), attrs={'__module__': '__main__', '__qualname__': 'User', 'id': <__main__.IntegerField object at 0x0000020AAB2D9C10>, 'name': <__main__.StringField object at 0x0000020AAB2D9CD0>, 'email': <__main__.StringField object at 0x0000020AAB2D9D60>, 'password': <__main__.StringField object at 0x0000020AAB2D9DC0>, 'hh': <function User.hh at 0x0000020AAB2DFEE0>}
Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
SQL: insert into User (id,username,email,password) values (?,?,?,?)
ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']


参考文献
廖雪峰的教程地址
讲解元类的视频

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值