Skip to main content

· One min read

Title: 第三版

  • 10.1 生成器

查看一个函数是否是生成器 isgeneratorfunction()

import inspect

inspect.isgeneratorfunction(foo)

获取生成器的当前状态 getgeneratorstate()

  1. GEN_CREATED 等待被第一次执行
  2. GEN_RUNNING 正在被执行
  3. GEN_SUSPENDED 等待被 next() 调用
  4. GEN_CLOSED 结束运行

· 2 min read

花无百日红,却有百态生,一态又一态,毁灭至重生!水有清浊,人有正邪,似清却非清,似邪却非邪,鱼目混珠境,慧眼观,慧眼连慧心,非眼,亦非耳,心净静,心无杂念,方能辨! —— Elewen

叶有常青绿,却无诗词颂,顾花忘落叶,花落拂袖去!人有离合,月有圆缺,似离却非离,似缺却非缺,泾渭分明时,现彼岸,彼岸有羞花,无色,亦无叶,花待叶,叶肥花红,登彼岸! —— Danyow

Title: 和有钱的文艺青年约会是什么样的体验?(已经被删)

墙外链接

章台柳,章台柳;昔日青青今在否? 东风恶,欢情薄;一杯愁绪,几年离索。 而今却悔当时错,当时错; 万事休休休莫莫; 一夜湘君白发多, 无人能唱采莲歌; 夜长人奈何。 人成各,今非昨; 今日岱宗夫如何。

· 10 min read

小白入门, 以前的笔记。

函数

函数的参数

  • 当默认参数为可变对象的时候 eg:
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)
  • 参数组合

目前共有五种参数

  1. 必选
  2. 默认
  3. 可变
  4. 关键字
  5. 命名关键字

定义必须遵循以上顺序

递归函数

  • 递归需要注意一点就是: 防止函数栈溢出

  • 尽可能的采用尾递归方式来处理递归函数

    • 尾递归:函数返回的时候, 只调用该函数本身, 不能添加其他的表达式.

高级特性

切片

  • 当如果采用-号来进行倒序取数的时候, 并不是取得顺序也是倒的, 而是正序.
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)

列表生成器

  • []

生成器: generator

  • () 或者 yield

迭代器: Iterator

  • 凡是可作用于for循环的对象都是Iterable类型;
  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
  • for循环本质上就是通过不断调用next()函数实现的

函数式编程

builtins: 内置函数

  • 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()

面向对象高级编程

使用slots

  • python的动态能力很强, 甚至可以给类添加绑定一个方法, 或者是给对象绑定一个参数. 但有问题就是想起限制作用, 这时候就可以使用到了slots
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]
'''

IO编程

序列化

  • 模型转json
# 自定义模式
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__)
  • json转模型
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锁,互不影响。

· One min read

eval()

eval(expression[, globals[, locals]])

  • 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 解析器。

· 7 min read

Title: 第二版

2. Python 对象

  • 自我解释 -> 在规范自己所编写的类的时候,应该加入详尽的注释。
class Point:
'这段是类注释,建议用单引号'

def __init__(self, x=0, y=0):
'''多行文档注释'''
pass

def foo(self):
'单行文档注释'
pass
  • 双下划线的命名的变量,是会对其实施命名改装。会自动封装 _(类名)

3. 对象相似时

  • 如果一个类继承了多个类,那么在初始化其父类时,可以采用如下方式。
# 分别调用父类
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) 的时候,需要注意

任意类的实例在被调用 insubclassininstance 是会去调用抽象基类重写的 __subclasshook__ 这个方法的,如果他说的是那么就是。

4. 异常捕获

  • elsefinally 的调用是,没有异常时前者会执行,后者无论如何都会执行。

  • 异常的层级

'''
BaseException <--- KeybordInterrupt, SystemExit
<--- Exception <--- 其他所有的异常
'''

6. Python 数据结构

  • 无法给内置类实例添加属性,但可以给自定义类添加属性。
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))
  • 队列有三种
  1. FIFO 队列(First In First Out

    from queue import Queue

  2. LIFO 队列(Last In First Out

    from queue import LifoQueue

  3. 优先级队列

    from queue import PriorityQueue

7. Python 面向对象的捷径

  • 上下文管理器,判断一个对象能否执行 with 语句,可以用哪个 dir() 查看里面是否有 __ennter____exit__

  • 参数解包,对于列表对象的解包是加 *, 字典的解包是 **

8. 字符串与序列化

  • 占位值改变打印样式
'''
{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
'''

9. 迭代器模式

  • 协程
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
'''
  1. yield 的出现和生成器暂停。
  2. 在函数外执行 send() 方法,且激活了生成器。
  3. 发送的值赋给了 yield 语句左侧的变量。
  4. 生成器继续执行,直至遇到下一个 yield 语句。

10. Python 设计模式 I

  • 观察者模式在执行起来需要以下两个步骤

    1. 被观察者主动通知观察者 通过 _update_observers

      class Inventory:
      def __init__(self):
      self.observers = []

      def _update_observers(self):
      for observer in self.observers:
      observer()
    2. 观察者自身现实被调用方法 通过 __call__

      class Observer:
      def __init__(self, inventory):
      self.inventory = inventory

      def __call__(self):
      print("观察成功!")

12. 测试面向对象程序

  • unittestsetUp() 是用于每次执行测试时,都会调用该方法用以保证数据被污染 和 tearDown() 是用来保证每次测试结束后调用。

  • py.test 与之相对应的有类似几个。

    1. setup_module
    2. teardown_module
    3. setup_class
    4. teardown_class
    5. setup_method
    6. teardown_method
  • pytest_funcarg__<idenntifier> 和被测试函数需要接受的参数作为测试

def pytest_funcarg__numbers(request):
return [1,2,3]

def test_sum(numbers):
assert sum(numbers) == 6

13. 并发

  • 自定义线程再被需要重写 run() 方法,但调用时用到的是 start()