NSF Back-end Dev Engineer

python

2023-04-10
nsf

请带上游标卡尺

1.基础知识

1.1.关键字

['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

1.2.类

内置方法

__delattr__: 删除属性
__dir__: 返回一个包含有所有属性名和方法名的有序列表
__eq__: 用来判断两个对象实例是否相等
__format__: 格式化实例时使用
__getattribute__: 属性访问拦截器,就是当这个类的属性被实例访问时,会自动调用类的__getattribute__方法
__ge__: 对象比较大小使用,相当于>=
__gt__: 对象比较大小使用,相当于>
__hash__: 生成唯一id,唯一标记对象
__init_subclass__: 当子类定义之后触发,
第一个参数是 cls 而不是常见的 self ,这是因为这个方法隐式地被 @classmethod 装饰(PEP-487)。
__init__ : 构造函数,在生成对象时调用
__le__: 对象比较大小使用,相当于<=
__lt__: 对象比较大小使用,相当于>=
@staticmethod __new__: 创建类实例的静态方法,优先 __init__() 初始化方法被调用
__ne__: 对象比较大小使用,相当于!=
__reduce_ex__: 用于pickle,可以指定版本
__reduce__: 用于pickle,不能指定版本
__repr__ : 打印,转换
__setattr__: 在对一个属性设置值的时候,会调用到这个函数
__sizeof__: 
__str__: str(self)
@classmethod __subclasshook__: 接收两个类,判断一个类是不是另一个类的子类
__class__: 类的实例当成函数调用
__dict__: 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性
__doc__: 注释内容
__module__: 对象在哪个模块

私有属性

类私有属性在类外面无法直接获取,举例

class A:
    def __init__(self, value):
        self.__value = value

    @property
    def value(self):
        return self.__value

obj = A(1)
obj.__value = 2
print(obj.value)
print(obj.__value)
print(obj._A__value)

输出:

1
2
1

多继承

例子

class A:
    def who(self):
        print('A', end='')

class B(A):
    def who(self):
        super(B, self).who()
        print('B', end='')

class C(A):
    def who(self):
        super(C, self).who()
        print('C', end='')

class D(B, C):
    def who(self):
        super(D, self).who()
        print('D', end='')

item = D()
item.who()

输出:ABCD

解释:

  1. Python 3中的类以及Python 2中的新式类使用C3算法来确定MRO,它是一种类似于广度优先搜索的方法;Python 2中的旧式类(经典类)使用深度优先搜索来确定MRO。在搞不清楚MRO的情况下,可以使用类的mro方法或__mro__属性来获得类的MRO列表。
  2. super()函数的使用。在使用super函数时,可以通过super(类型, 对象)来指定对哪个对象以哪个类为起点向上搜索父类方法。所以上面B类代码中的super(B, self).who()表示以B类为起点,向上搜索self(D类对象)的who方法,所以会找到C类中的who方法,因为D类对象的MRO列表是D --> B --> C --> A --> object

由上可以看出,多继承时MRO顺序不好判断,容易出错,特别是使用第三方库时(还得看源码),所以自己设计时一方面是尽量避免使用多继承,如果没办法一定要使用多继承,在重写父类方法时需要特别注意MRO顺序。

函数属性

__annotations__: 参数和返回值注解
__closure__: 函数闭包,自由变量的绑定(通常为None)
__code__: 编译成字节码的函数元数据和函数定义体
__defaults__: 形式参数的默认值
__get__: 实现只读描述符协议
__global__: 函数所在模块中的全局变量
__kwdefaults__: 仅限关键字形式参数的默认值
__name__: 函数名称
__qualname__: 函数限定名称

可附加的一些 方法

1.3.字符串

Python正则替换字符串函数re.sub

命令:re.sub(pattern, repl, string, count=0, flags=0)
re.sub 用于替换字符串的匹配项。如果没有匹配到规则,则原字符串不变。

第一个参数:规则 
第二个参数:替换后的字符串 
第三个参数:字符串 
第四个参数:替换个数。默认为0,表示每个匹配项都替换

1.4.list

list做减法操作

问题描述:假设我有这样两个list,
          一个是list1,list1 = [1, 2, 3, 4, 5]
          一个是list2,list2 = [1, 4, 5] 
          我们如何得到一个新的list,list3,
          list3中包括所有不在list2中出现的list1中的元素。
          即:list3 = list1 - list2
          
解决方案:我们可以用set(集合)操作
          list3 = list(set(list1) - set(list2))
          
          set操作会将一个list转换成一个集合。
          假设:list_t = [1, 2, 3, 1]
          那么:list(set(list_t)) = [1, 2, 3]
          是的,重复的项会被删除。

其他方案:list3 = [i for i in list1 if i not in list2]       #可用于list分片
          这样写也很直观。
          但是在list很大的时候,没有set方法快。

额外知识:a = [[i,j] for i in range(2) for j in range(3)]
          这个和“其他方案”非常相似,
          都是在[]里面进行判断和计算。
          感觉很方便,至少比下面这样要方便、要快:
          a = []
          for i in range(2):
              for j in range(3):
                  a.append([i,j])

某些问题:如果list_tmp = [[1,2],[2,3]]
          即list里面不是单独的元素组成,而是list,
          那么set(list_tmp)会出问题。

1.5.import

from xx import *形式容易造成命名冲突,建议慎用!

主模块的名称在运行时会被Python强制命名为__main__,相对导入方式可以解决导入当前目录、上级目录、上上级目录下模块的需求,但使用相对导入的文件不能直接以脚本方式运行,只能以模块方式(-m选项)运行,并且当前路径要处于import语句定位的路径或更高层路径

pip freeze > requirements.txt生成该文件

ip install -r requirements.txt安装该文件所有库

循环导入

目前有2个python文件a.py,b.py,一个配置文件d.yaml

  1. a文件需要导入d文件配置,b文件依赖a文件的变量a1,负责重写d文件
  2. a文件中有个逻辑中需要重写d文件,所以也导入了b文件,调用了b文件中的重写逻辑
  3. 这样就导致了a-b-a的循环导入

解决方案:

  • 新建c.py文件
  • a文件中变量a1移入c文件
  • 至此b文件不依赖a文件,改为了依赖c文件,打破了循环依赖

总结:

循环依赖在复杂项目中容易出现,解决方案都是打破循环,重点是需要理清逻辑,将循环依赖部分隔离出去

1.6.异常

使用异常时如果没有引发异常是很快的,但是如果会时常引发异常,建议使用if-else,因为捕获异常是很昂贵的。

1.7.循环

迭代器每次取出n个元素

比如每次取出两个元素

nums = [1, 2, 3, 4, 5]
num_iter = iter(nums)
for o, e in zip_longest(*([num_iter] * 2), fillvalue=0):
    print(o, e)

这里有一个hack的写法,就是[num_iter] * 2

  • 利用了iter的性质,每次是取迭代器下一个元素
  • *([num_iter] * 2)进行了解包,也就是解成了两个参数,注意这两个参数其实是同一个对象num_iter
  • zip_longest将这两个参数每次一个参数取一个元素,也就是num_iter每次取两个元素

1.8.pip

离线安装包

  1. 下载

    pip download -d 目录 模块名(-r requestments.txt)
    
  2. 安装

    pip install --no-index --find-links=d:\packages 模块名 (-r requirements.txt)
    

安装指定平台指定python版本的包

pip download -d . --only-binary=:all: --platform manylinux1_x86_64 --python-version 36 sklearn==0.0

1.9.函数

函数参数谨慎使用列表字典对象,举例

def extend_list(val, items=[]):
    items.append(val)
    return items

list1 = extend_list(10)
list2 = extend_list(123, [])
list3 = extend_list('a')
print(list1)
print(list2)
print(list3)

输出:

[10, 'a']
[123]
[10, 'a']

Python函数在定义的时候,默认参数items的值就被计算出来了,即[]。因为默认参数items引用了对象[],每次调用该函数,如果对items引用的列表进行了操作,下次调用时,默认参数还是引用之前的那个列表而不是重新赋值为[]

2.附加知识

2.1.反编译

安装反编译库,命令如下

pip install uncompyle

uncompyle6 –help 查看帮助 uncompyle6 models.pyc > models.py 将models.pyc反编译成py文件 uncompile -o . *.pyc 将当前文件夹中所有的pyc文件反编译成后缀名为.pyc_dis的源文件

2.2.缓存

介绍functools.lru_cache使用,我们先看下区别,测试代码如下

import timeit
from functools import lru_cache

@lru_cache()
def f1(n):
    return 0 if n == 0 else(1 if n == 1 else f1(n - 1) + f1(n - 2))
def f2(n):
    return 0 if n == 0 else(1 if n == 1 else f2(n - 1) + f2(n - 2))
print(timeit.timeit('f2(40)', globals=globals(), number=1))

f2(40)耗时60.84906945534581s,f1(40)耗时6.381289836242231e-05s,区别巨大。

@lru_cache()使能够“被冻结”的函数缓存,能够“被冻结”的指的是:在函数参数不变的情况下,函数返回值不变,从而大大提升了效率。

2.3.元类

元类是用来创建类的

2.4.functools

partial:用来”冻结”一个函数的参数,并返回”冻结”参数后的新函数,比如timeit.timeit需要使用类方法时,需要用partial:

class Solution(object):
    def f(self, x: int, y: int) -> int:
        x = x ^ y
timeit.timeit(partial(s.f, x, y))

2.5.itertools

itertools.accumulate(iterable[, func])

创建一个迭代器,返回累加和或其他二元函数的累加结果(通过可选参数 func 指定)。如果提供了 func ,它应是2个参数的函数。输入 iterable 元素类型应是 func 能支持的任意类型。如果可迭代对象的输入为空,输出也为空。

大致相当于:

def accumulate(iterable, func=operator.add):
    'Return running totals'
    # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
    # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
    it = iter(iterable)
    try:
        total = next(it)
    except StopIteration:
        return
    yield total
    for element in it:
        total = func(total, element)
        yield total

func 参数有几种用法。它可以被设为 min() 最终得到一个最小值,或者设为 max() 最终得到一个最大值,或设为 operator.mul() 最终得到一个乘积。

>>> data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
>>> list(accumulate(data, operator.mul))     # running product
[3, 12, 72, 144, 144, 1296, 0, 0, 0, 0]
>>> list(accumulate(data, max))              # running maximum
[3, 4, 6, 6, 6, 9, 9, 9, 9, 9]

# Amortize a 5% loan of 1000 with 4 annual payments of 90
>>> cashflows = [1000, -90, -90, -90, -90]
>>> list(accumulate(cashflows, lambda bal, pmt: bal*1.05 + pmt))
[1000, 960.0, 918.0, 873.9000000000001, 827.5950000000001]

2.6.调用linux系统命令

python调用linux命令方式有好几种,比如os.system和subprocess的一些函数等,这些实际上都是对c语言调用linux系统命令的包装而已,有时会遇到使用python调用linux命令和自己在shell中调用出现不一致的情况,下面举个例子来说明可能有哪些区别。

  事情的起因是这样的,近日接到一个需求要将md转doc,于是用到了pandoc这个库,首先我本地安装了pandoc,使用体验不错,顺便学习了md的标准法。
  本地使用pycharm,在python中使用pypandoc来进行文件转换,结果发现内存暴涨的问题,后面看了pypandoc代码,发现是使用subprocess.Popen调用linux下pandoc相应命令来运行,所以理论上应该和再shell中调用pandoc命令效果一致才对。
  接下来查看进程详情才发现python使用的是/bin/sh,而我自己用的是/bin/bash,于是将/bin/sh的软链接改成了/bin/bash,但是问题仍未解决,最后抱着试一试的态度在命令行中运行了python脚本,结果发现竟然问题解决了。
  后面本地将/bin/sh软链接恢复后发现问题解决,最终确定问题根源只有pycharm。
  总结:虽然上面的例子中的问题根源不是由python调用linux命令引起,但是在定位问题的过程中能发现python调用linux命令是会指定某个shell来运行命令的,这可能会导致一些问题,需要我们额外注意。

2.7.hook

目的是为了预留功能,比如在一件事情做之前,可以选择做事情A(当然也可以不做),那么可以实现这个一个事情A,这就是一个hook,举例:

class Watermelon:
    def __init__(self):
        self.say_it_hook = None

    def register_say_it_hook(self, say_it_hook):
        self.say_it_hook = say_it_hook

    def say_it(self, content):
        if self.say_it_hook is not None:
            self.say_it_hook(content)
        print('so big')


watermelon = Watermelon()
watermelon.register_say_it_hook(lambda x: print(x))
watermelon.say_it('have fun')

3.常用场景

3.1 获取可迭代对象中满足条件的第一个元素

next((_ for _ in range(100) if _ %2 == 0), 1))

Similar Posts

上一篇 aarch64+php7+svm

下一篇 后端框架

Comments