跳到主要内容

6 篇博文 含有标签「python」

查看所有标签

· 4 分钟阅读

Title: Bilibili

P8

  • 使用字符串来导入类
__import__(name, globals=None, locals=None, fromlist=(), level=0)
  • config 的配置文件路径默认在根目录下,需要更改目录的话,要查看对应的Api文档。

P9

  • __name__ 函数调用的话,可以获取这个函数的名称。

P18

  • 怎么用父类去约束子类必须实现某方法,意指接口(interface)。
class Base(object):
def play():
raise NoImplementedError('必须实现该方法')
  • 顺带一提 P18 这一讲当中大概是 20 几分钟左右讲到的配置去做某事的思路对我启发蛮大。并且有助于理解 Django 的中间件。

  • 装饰器可以有多个,下面的会和被装饰的函数当成 inner 加入到上层的装饰器当中去。

  • endpoint 的意义目前理解为 id

P19

  • 利用 functools.wraps() 来帮助我们设置函数的元信息
import functools

def wapper(func):
@functools.wapper(func) # 保存元信息
def inner(*args, **kw):
return func(*args, **kw)
return inner

@wapper
def foo():
pass

print(foo.__name__)

'''
如果不设置元信息的话,会导致打印的是 inner
设置之后才可以确定是原先的函数
'''

P20

  • 闪现:类似于暂时存储部分内容的快照,取出来就没了。(阅后即焚)

    应用场景:对临时数据的操作,例如错误信息。

  • 请求扩展,指的是在请求之前和响应之前的监听,可以有多个监听,请求之前的监听是顺序执行,响应的是倒序执行。

    @app.before_request
    def before_request_1(*args, **kw):
    print("before_request_1 进来了")

    @app.before_request
    def before_request_2(*args, **kw):
    print("before_request_2 进来了")

    @app.after_request
    def after_request_1(response):
    print("after_request_1 走了")

    @app.after_request
    def after_request_2(response):
    print("after_request_2 走了")

    '''
    打印顺序为
    before_request_1 进来了
    before_request_2 进来了
    ...
    after_request_2 走了
    after_request_1 走了
    '''

    如果请求拦截之后,在他之后的请求就不会执行了,但响应的还是会继续执行。

  • 根据状态码定义错误信息

    @app.errorhandler(404)
    def error_404(args):
    return "404页面没找到"
  • before_first_request 第一个请求

    @app.before_first_request
    def first(*args, **kw):
    ## 连接数据库之类的
    pass

P22

  • 中间件的讲解 P22

P25

  • threading.local() 给每个线程开辟一个内存空间用以对应的线程执行相应操作。达到类似加锁的目的。

P36

  • 数据的链接池库采用 DBUtils ,但如果使用的是 PooledDB 模式的话,其中有个参数需要注意 maxshared : 链接池中最多共享的链接数量。0 为全部共享,但这个值设置的时候是由 creatorthreadsafety 来判别的,也就是说设置后不一定会生效。

· 1 分钟阅读

Title: 第三版

  • 10.1 生成器

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

import inspect

inspect.isgeneratorfunction(foo)

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

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

· 10 分钟阅读

小白入门, 以前的笔记。

函数

函数的参数

  • 当默认参数为可变对象的时候 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锁,互不影响。

· 1 分钟阅读

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 分钟阅读

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