Title: 第三版
- 10.1 生成器
查看一个函数是否是生成器 isgeneratorfunction()
import inspect
inspect.isgeneratorfunction(foo)
获取生成器的当前状态 getgeneratorstate()
GEN_CREATED
等待被第一次执行GEN_RUNNING
正在被执行GEN_SUSPENDED
等待被next()
调用GEN_CLOSED
结束运行
Title: 第三版
查看一个函数是否是生成器 isgeneratorfunction()
import inspect
inspect.isgeneratorfunction(foo)
获取生成器的当前状态 getgeneratorstate()
GEN_CREATED
等待被第一次执行GEN_RUNNING
正在被执行GEN_SUSPENDED
等待被 next()
调用GEN_CLOSED
结束运行花无百日红,却有百态生,一态又一态,毁灭至重生!水有清浊,人有正邪,似清却非清,似邪却非邪,鱼目混珠境,慧眼观,慧眼连慧心,非眼,亦非耳,心净静,心无杂念,方能辨! —— Elewen
叶有常青绿,却无诗词颂,顾花忘落叶,花落拂袖去!人有离合,月有圆缺,似离却非离,似缺却非缺,泾渭分明时,现彼岸,彼岸有羞花,无色,亦无叶,花待叶,叶肥花红,登彼岸! —— Danyow
Title: 和有钱的文艺青年约会是什么样的体验?(已经被删)
章台柳,章台柳;昔日青青今在否? 东风恶,欢情薄;一杯愁绪,几年离索。 而今却悔当时错,当时错; 万事休休休莫莫; 一夜湘君白发多, 无人能唱采莲歌; 夜长人奈何。 人成各,今非昨; 今日岱宗夫如何。
小白入门, 以前的笔记。
def add_end(L=[]):
L.append('END')
return L
'''
如果多次调用该函数
就会导致L的值不断递增
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
因为L也是一个变量
所以最佳做法是将 L=None
在函数内部去做判断
'''
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
'''
# 当如果要使用list传入的话
'''
nums = [1, 2, 3]
calc(*nums)
# 定义
**kw
#调用
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
# 无可变参数时 必须加入*分隔
def person(name, age, *, city, job):
print(name, age, city, job)
# 有可变参数时 无序理会
def person(name, age, *args, city, job):
print(name, age, args, city, job)
目前共有五种参数
定义必须遵循以上顺序
递归需要注意一点就是: 防止函数栈溢出
尽可能的采用尾递归方式来处理递归函数
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
L[-2:]
# 这个-2指从倒数第二个开始, 继续往后取值, 而不是往前取值.
# 即: ['Bob', 'Jack']
# 而不是 ['Bob', 'Tracy', 'Sarah', 'Michael']
字典直接遍历时取得是所有的键, 需要字典的所有值是 dic.values()
, 所有键值对是 dic.items()
Iterable
: 可迭代 from collections import Iterable
isinstance
: 判断一个对象的类型 isinstance(object, class-or-type-or-tuple)
enumerate
将一个list改变为索引-元素对
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9
for ... in
是不支持直接遍历字典的时候, 同时取到键和值需要以下方法# 会报错
d = {'key_1':'1', 'key_2':'2', 'key_3':'3'}
for key, value in d:
print(key + value)
# 加上items这样就不会报错了
d = {'key_1':'1', 'key_2':'2', 'key_3':'3'}
for key, value in d.items():
print(key + value)
[]
()
或者 yield
for
循环的对象都是Iterable
类型;next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;list、dict、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。for
循环本质上就是通过不断调用next()
函数实现的abs
就在import builtins
map
对每个数进行一次函数算法, 然后重新组装为一个数组.
reduce
从 0, 1 每两个, 直到最后一个数进行一次函数算法, 然后返回.
filter
遍历每个数对其进行过滤 真保存, 假删除.
以上都是返回的是
Iterator
类型 需要用list()
让其算完.
sys.argv
存储了命令行输入参数, 当长度为1时没有输入参数.
if __name__=='__main__':
被引入使用时失效, 仅在运行当前文件时生效, 主要用于测试.
PYTHONPATH
# 仅仅在运行时添加
import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
获取对象所有方法 dir()
类似OC动态运行时 getattr()
setattr()
hasattr()
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
'''
这里补充下网友发现的一些现象
1. 如果设置了__slots__ 但是给类直接绑定新的值, 还是可行 eg: Student.score = 99
2. 如果采用了__slots__内含有双下划线的变量时, 在继承的时候该 变量会变形 eg: Person __slots__('__name') Student(Person) __slots__() 这时候的Student的name在用的时候会报错. 由于其没有改写本身的__name, 所以已经变形为_Persion__name
'''
__init__
初始化的时候使用__len__
len()
方法的时候__str__
print()
方法的时候__repr__
命令行直接输出的时候__iter__
返回迭代所需要的对象__next__
返回下一个取出来的值__getitem__
[::]
的方式取下标__getattr__
通常用作获取一个没有的值得属性的时候__call__
判断是否可调用的具体条件callable()
type()
直接去创建.def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
metaclass
本质上就是监听创建这个类时的回调, 从而在本源上大规模的去改动这个类的创建形式.class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
pass
'''
>>> L = MyList()
>>> L.add(1)
>> L
[1]
'''
# 自定义模式
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
json.dumps(s, default=student2dict)
# 偷懒模式
json.dumps(s, default=lambda obj: obj.__dict__)
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
json.loads(json_str, object_hook=dict2student)
GIL
启动与CPU核心数量相同的N个线程,在4核CPU上可以监控到CPU占用率仅有102%,也就是仅使用了一核。
但是用C、C++或Java来改写相同的死循环,直接可以把全部核心跑满,4核就跑到400%,8核就跑到800%,为什么Python不行呢?
因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。
所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。
不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
eval()
eval(expression[, globals[, locals]])
>>>x = 7
>>> eval( '3 * x' )
21
>>> eval('pow(2,2)')
4
>>> eval('2 + 2')
4
>>> n=81
>>> eval("n + 4")
85
就好像是在 python 语言里面自建了一个 python 解析器。
Title: 第二版
class Point:
'这段是类注释,建议用单引号'
def __init__(self, x=0, y=0):
'''多行文档注释'''
pass
def foo(self):
'单行文档注释'
pass
# 分别调用父类
class Teacher(Person, Title):
def __init__(self, name, title, studens):
Person.__init__(self, name)
Title.__init__(self, title)
self.studens = studens
'''
以上就是简单定义了一个多继承关系的类,这么看来是没什么问题的
但作者提到了一个问题,如果在定义模型的时候,需要连接数据库,那么每次都是连接两次数据库。
这里可以采用 __mro__ 来修改方法的调用顺序
'''
# 采用super() 和 **kw 的方式
class Person(object):
def __init__(self, name='', **kw):
self.name = name
class Title(object):
def __init__(self, title='', **kw):
self.title = title
class Teacher(Person, Title):
def __init__(self, studens=null, **kw):
super().__init__(**kw)
self.studens = studens
'''
super() 的方式可以确保object只初始化一次。
**kw 的方式可以解决多父类情况下 参数不一致的问题
'''
abc
而且这个类重写了 __subclasshook__(cls, C)
的时候,需要注意任意类的实例在被调用 insubclass
和 ininstance
是会去调用抽象基类重写的 __subclasshook__
这个方法的,如果他说的是那么就是。
else
和 finally
的调用是,没有异常时前者会执行,后者无论如何都会执行。
异常的层级
'''
BaseException <--- KeybordInterrupt, SystemExit
<--- Exception <--- 其他所有的异常
'''
o = object()
o.x = 0
'''
👆会报错
👇正常运行
'''
class MyClass(object):
pass
m = MyClass()
m.x = 1
from collections import namedtuple
Stock = namedtuple("Stock", "symbol current high low")
# Stock = namedtuple("Stock", "symbol, current, high, low")
stock = Stock("FB", 75.0, 75.3, 74.2)
# 可以使用点语法
stock.symbol
total_ordering
对应着 >, <, ==, !=, >=, <=
逻辑运算符
operator.itemgetter
可以对元组排序时,直接修改所要比较时用到的是第几个属性
from operator import itemgetter
l = [('h', 4), ('n', 6), ('o', 5), ('p', 1), ('t', 3), ('y', 2)]
# 这里直接采用第二个键来做比较的项
l.sort(key=itemgetter(1))
FIFO 队列(First In First Out)
from queue import Queue
LIFO 队列(Last In First Out)
from queue import LifoQueue
优先级队列
from queue import PriorityQueue
上下文管理器,判断一个对象能否执行 with
语句,可以用哪个 dir()
查看里面是否有 __ennter__
和 __exit__
参数解包,对于列表对象的解包是加 *
, 字典的解包是 **
'''
{0:10s} s字符串,然后是占10个字节。
{1:^9d} d整形,然后占9个字节,^(脱字符)表示居中对齐。
{2:<8.2f} f浮点型,然后占8个字节,<表示向左对齐,.2省略两位小数
{3:>7.2f} f浮点型,然后占7个字节,>表示向右对齐,.2省略两位小数
'''
orders = [('burger', 2, 5),
('fries', 3.5, 1),
('cola', 1.75, 3)]
print("PRODUCT QUANTITY PRICE SUBTOTAL")
for product, price, quantity in orders:
subtotal = price * quantity
print("{0:10s}{1:^9d} ${2:<8.2f}${3:>7.2f}".format(product, quantity, price, subtotal))
'''
PRODUCT QUANTITY PRICE SUBTOTAL
burger 5 $2.00 $ 10.00
fries 1 $3.50 $ 3.50
cola 3 $1.75 $ 5.25
'''
def tally():
score = 0
while True:
increment = yield score
print("increment : {}, score : {}".format(increment, score))
score += increment
white_sox = tally()
next(white_sox)
white_sox.send(3)
'''
increment : 3, score : 0
'''
yield
的出现和生成器暂停。send()
方法,且激活了生成器。yield
语句左侧的变量。yield
语句。观察者模式在执行起来需要以下两个步骤
被观察者主动通知观察者 通过 _update_observers
class Inventory:
def __init__(self):
self.observers = []
def _update_observers(self):
for observer in self.observers:
observer()
观察者自身现实被调用方法 通过 __call__
class Observer:
def __init__(self, inventory):
self.inventory = inventory
def __call__(self):
print("观察成功!")
unittest
的 setUp()
是用于每次执行测试时,都会调用该方法用以保证数据被污染 和 tearDown()
是用来保证每次测试结束后调用。
py.test
与之相对应的有类似几个。
setup_module
teardown_module
setup_class
teardown_class
setup_method
teardown_method
pytest_funcarg__<idenntifier>
和被测试函数需要接受的参数作为测试
def pytest_funcarg__numbers(request):
return [1,2,3]
def test_sum(numbers):
assert sum(numbers) == 6
run()
方法,但调用时用到的是 start()