列表生成式和列表生成器
列表生成式
[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']