1.简述解释型和编译型编程语言
解释型语言是在运行程序的时候才翻译,每执行一次,要翻译一次,效率较低。 编译型就是直接编译成机型可以执行的,只翻译一次,所以效率相对来说较高
2. Python3 和 Python2 的区别
Python 2的str类型是字节串(ASCII),同3中的bytes,Python3 中默认的字符串类型是 Unicode。
Python 2中的print和exec都是关键字,Python 3中变成了函数,必须加括号。
Python 2中的不等号<>在Python 3中被 != 取代。
Python 2中的xrange函数在Python 3中被range函数取代。
Python 2中raw_input函数在Python3 中被input函数取代。
Python 2中的file函数被Python 3中的open函数取代。
Python 2中的/运算对于int类型是整除,Python3 中是浮点类型,在Python 3中要用//来做整除除法。
Python 2中的round函数返回float类型,Python 3中的round函数可以返回int或float类型。
Python 3中的比较运算符必须比较同类对象。
Python 3中定义类的都是新式类,Python 2中定义的类有新式类(显式继承自object的类)和旧式类(经典类)之分,新式类和旧式类在MRO问题上有非常显著的区别,新式类可以使用__class__属性获取自身类型,新式类可以使用__slots__魔法。
Python 3对代码缩进的要求更加严格,如果混用空格和制表键会引发TabError。
Python 3中字典的keys、values、items方法都不再返回list对象,而是返回view object,内置的map、filter等函数也不再返回list对象,而是返回迭代器对象。
Python 3标准库中某些模块和 2是有区别的;第三方库有些只支持2,有些只能支持 3。
Python 3中没有long类型,整数都是int类型。
Python 3中改进了Python 2捕获异常的代码
Python 3生成式中循环变量的作用域得到了更好的控制,不会影响到生成式之外的同名变量。
3. Python中的实例方法、静态方法和类方法有什么区别?
实例方法:接受self参数,并且与类的特定实例相关。
静态方法:使用装饰器 @staticmethod,与特定实例无关,并且是自包含的(不能修改类或实例的属性)。
类方法:接受cls参数,并且可以修改类本身
4. 类中的“self”指的是什么?
“self”引用类本身的实例。这就是我们赋予方法访问权限并且能够更新方法所属对象的能力。
5. 在Python中如何实现单例模式
# 装饰器
from functools import wraps
def singleton(cls):
"""单例类装饰器"""
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class President:
pass
# 元类
class SingletonMeta(type):
"""自定义单例元类"""
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
pass
6. 不使用中间变量,交换两个变量a和b的值。
# way 1
a = a ^ b
b = a ^ b
a = a ^ b
# way 2
a,b = b,a
7. 写一个删除列表中重复元素的函数,要求去重后元素相对位置保持不变。
# way1
def dedup(items):
no_dup_items = []
seen = set()
for item in items:
if item not in seen:
no_dup_items.append(item)
seen.add(item)
return no_dup_items
# way2
def dedup(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
8. Lambda函数是什么,举例说明的它的应用场景
Lambda函数也叫匿名函数,它是功能简单用一行代码就能实现的小型函数。Python中的Lambda函数只能写一个表达式,这个表达式的执行结果就是函数的返回值,不用写return关键字。Lambda函数因为没有名字,所以也不会跟其他函数发生命名冲突的问题。
Lambda函数其实最为主要的用途是把一个函数传入另一个高阶函数(如Python内置的filter、map等)中来为函数做解耦合,增强函数的灵活性和通用性。下面的例子通过使用filter和map函数,实现了从列表中筛选出奇数并求平方构成新列表的操作,因为用到了高阶函数,过滤和映射数据的规则都是函数的调用者通过另外一个函数传入的,因此这filter和map函数没有跟特定的过滤和映射数据的规则耦合在一起。
9. Python中的浅拷贝和深拷贝
浅拷贝通常只复制对象本身,而深拷贝不仅会复制对象,还会递归的复制对象所关联的对象。深拷贝可能会遇到两个问题:一是一个对象如果直接或间接的引用了自身,会导致无休止的递归拷贝;二是深拷贝可能对原本设计为多个对象共享的数据也进行拷贝。Python通过copy模块中的copy和deepcopy函数来实现浅拷贝和深拷贝操作,其中deepcopy可以通过memo字典来保存已经拷贝过的对象,从而避免刚才所说的自引用递归问题;此外,可以通过copyreg模块的pickle函数来定制指定类型对象的拷贝行为。列表的切片操作[:]相当于实现了列表对象的浅拷贝,而字典的copy方法可以实现字典对象的浅拷贝。对象拷贝其实是更为快捷的创建对象的方式。在Python中,通过构造器创建对象属于两阶段构造,首先是分配内存空间,然后是初始化。在创建对象时,我们也可以基于“原型”对象来创建新对象,通过对原型对象的拷贝(复制内存)就完成了对象的创建和初始化,这种做法更加高效,这也就是设计模式中的原型模式。在Python中,我们可以通过元类的方式来实现原型模式
import copy
class PrototypeMeta(type):
"""实现原型模式的元类"""
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
# 为对象绑定clone方法来实现对象拷贝
cls.clone = lambda self, is_deep=True: \
copy.deepcopy(self) if is_deep else copy.copy(self)
class Person(metaclass=PrototypeMeta):
pass
p1 = Person()
p2 = p1.clone() # 深拷贝
p3 = p1.clone(is_deep=False) # 浅拷贝
10. Python是如何实现内存管理的?
Python提供了自动化的内存管理,也就是说内存空间的分配与释放都是由Python解释器在运行时自动进行的,自动管理内存功能极大的减轻程序员的工作负担,也能够帮助程序员在一定程度上解决内存泄露的问题。以CPython解释器为例,它的内存管理有三个关键点:引用计数、标记清理、分代收集。
引用计数:对于CPython解释器来说,Python中的每一个对象其实就是PyObject结构体,它的内部有一个名为ob_refcnt 的引用计数器成员变量。程序在运行的过程中ob_refcnt的值会被更新并藉此来反映引用有多少个变量引用到该对象。当对象的引用计数值为0时,它的内存就会被释放掉。
标记清理:CPython使用了“标记-清理”(Mark and Sweep)算法解决容器类型可能产生的循环引用问题。该算法在垃圾回收时分为两个阶段:标记阶段,遍历所有的对象,如果对象是可达的(被其他对象引用),那么就标记该对象为可达;清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。CPython底层维护了两个双端链表,一个链表存放着需要被扫描的容器对象(姑且称之为链表A),另一个链表存放着临时不可达对象(姑且称之为链表B)。为了实现“标记-清理”算法,链表中的每个节点除了有记录当前引用计数的ref_count变量外,还有一个gc_ref变量,这个gc_ref是ref_count的一个副本,所以初始值为ref_count的大小。执行垃圾回收时,首先遍历链表A中的节点,并且将当前对象所引用的所有对象的gc_ref减1,这一步主要作用是解除循环引用对引用计数的影响。再次遍历链表A中的节点,如果节点的gc_ref值为0,那么这个对象就被标记为“暂时不可达”(GC_TENTATIVELY_UNREACHABLE)并被移动到链表B中;如果节点的gc_ref不为0,那么这个对象就会被标记为“可达“(GC_REACHABLE),对于”可达“对象,还要递归的将该节点可以到达的节点标记为”可达“;链表B中被标记为”可达“的节点要重新放回到链表A中。在两次遍历之后,链表B中的节点就是需要释放内存的节点。
分代回收:在循环引用对象的回收中,整个应用程序会被暂停,为了减少应用程序暂停的时间,Python 通过分代回收(空间换时间)的方法提高垃圾回收效率。分代回收的基本思想是:对象存在的时间越长,是垃圾的可能性就越小,应该尽量不对这样的对象进行垃圾回收。CPython将对象分为三种世代分别记为0、1、2,每一个新生对象都在第0代中,如果该对象在一轮垃圾回收扫描中存活下来,那么它将被移到第1代中,存在于第1代的对象将较少的被垃圾回收扫描到;如果在对第1代进行垃圾回收扫描时,这个对象又存活下来,那么它将被移至第2代中,在那里它被垃圾回收扫描的次数将会更少。分代回收扫描的门限值可以通过gc模块的get_threshold函数来获得,该函数返回一个三元组,分别表示多少次内存分配操作后会执行0代垃圾回收,多少次0代垃圾回收后会执行1代垃圾回收,多少次1代垃圾回收后会执行2代垃圾回收。需要说明的是,如果执行一次2代垃圾回收,那么比它年轻的代都要执行垃圾回收。如果想修改这几个门限值,可以通过gc模块的set_threshold函数来做到。
11. Python中迭代器和生成器
迭代器是实现了迭代器协议的对象。跟其他编程语言不同,Python中没有用于定义协议或表示约定的关键字,像interface、protocol这些单词并不在Python语言的关键字列表中。Python语言通过魔法方法来表示约定,也就是我们所说的协议,而__next__和__iter__这两个魔法方法就代表了迭代器协议。可以通过for-in循环从迭代器对象中取出值,也可以使用next函数取出迭代器对象中的下一个值。生成器是迭代器的语法升级版本,可以用更为简单的代码来实现一个迭代器。
# 迭代器
class Fib(object):
def __init__(self, num):
self.num = num
self.a, self.b = 0, 1
self.idx = 0
def __iter__(self):
return self
def __next__(self):
if self.idx < self.num:
self.a, self.b = self.b, self.a + self.b
self.idx += 1
return self.a
raise StopIteration()
# 生成器
def fib(num):
a, b = 0, 1
for _ in range(num):
a, b = b, a + b
yield a
12. 正则表达式的match方法和search方法有什么区别
使用正则表达式有两种方式,一种是直接调用re模块中的函数,传入正则表达式和需要处理的字符串;一种是先通过re模块的compile函数创建正则表达式对象,然后再通过对象调用方法并传入需要处理的字符串。如果一个正则表达式被频繁的使用,推荐用re.compile函数创建正则表达式对象,这样会减少频繁编译同一个正则表达式所造成的开销。match方法是从字符串的起始位置进行正则表达式匹配,返回Match对象或None。search方法会扫描整个字符串来找寻匹配的模式,同样也是返回Match对象或None。
13. Python中为什么没有函数重载
Python是解释型语言,函数重载现象通常出现在编译型语言中。其次Python是动态类型语言,函数的参数没有类型约束,也就无法根据参数类型来区分重载。再者Python中函数的参数可以有默认值,可以使用可变参数和关键字参数,因此即便没有函数重载,也要可以让一个函数根据调用者传入的参数产生不同的行为。
14. 实现Python内置函数max
def my_max(*args, key=None, default=None):
"""
获取可迭代对象中最大的元素或两个及以上实参中最大的元素
:param args: 一个可迭代对象或多个元素
:param key: 提取用于元素比较的特征值的函数,默认为None
:param default: 如果可迭代对象为空则返回该默认值,如果没有给默认值则引发ValueError异常
:return: 返回可迭代对象或多个元素中的最大元素
"""
if len(args) == 1 and len(args[0]) == 0:
if default:
return default
else:
raise ValueError('max() arg is an empty sequence')
items = args[0] if len(args) == 1 else args
max_elem, max_value = items[0], items[0]
if key:
max_value = key(max_value)
for item in items:
value = item
if key:
value = key(item)
if value > max_value:
max_elem, max_value = item, value
return max_elem
15. 写一个函数统计传入的列表中每个数字出现的次数并返回对应的字典
# way 1
def count_letters(items):
result = {}
for item in items:
if isinstance(item, (int, float)):
result[item] = result.get(item, 0) + 1
return result
# way 2
from collections import Counter
def count_letters(items):
counter = Counter(items)
return {key: value for key, value in counter.items() \
if isinstance(key, (int, float))}
16. 使用Python代码实现遍历一个文件夹的操作
import os
g = os.walk('/Users/Hao/Downloads/')
for path, dir_list, file_list in g:
for dir_name in dir_list:
print(os.path.join(path, dir_name))
for file_name in file_list:
print(os.path.join(path, file_name))
17. 现有2元、3元、5元共三种面额的货币,如果需要找零99元,一共有多少种找零的方式
from functools import lru_cache
@lru_cache()
def change_money(total):
if total == 0:
return 1
if total < 0:
return 0
return change_money(total - 2) + change_money(total - 3) + \
change_money(total - 5)
18. 给定矩阵的阶数n,输出一个螺旋式数字矩阵
def show_spiral_matrix(n):
matrix = [[0] * n for _ in range(n)]
row, col = 0, 0
num, direction = 1, 0
while num <= n ** 2:
if matrix[row][col] == 0:
matrix[row][col] = num
num += 1
if direction == 0:
if col < n - 1 and matrix[row][col + 1] == 0:
col += 1
else:
direction += 1
elif direction == 1:
if row < n - 1 and matrix[row + 1][col] == 0:
row += 1
else:
direction += 1
elif direction == 2:
if col > 0 and matrix[row][col - 1] == 0:
col -= 1
else:
direction += 1
else:
if row > 0 and matrix[row - 1][col] == 0:
row -= 1
else:
direction += 1
direction %= 4
for x in matrix:
for y in x:
print(y, end='\t')
print()
19. 列表推导式
items = [1, 2, 3, 4]
print([i for i in items if i > 2])
print([i for i in items if i % 2])
print([(x, y) for x, y in zip('abcd', (1, 2, 3, 4, 5))])
print({x: f'item{x ** 2}' for x in (2, 4, 6)})
print(len({x for x in 'hello world' if x not in 'abcdefg'}))
20. __init__和__new__方法有什么区别
Python中调用构造器创建对象属于两阶段构造过程,首先执行__new__方法获得保存对象所需的内存空间,再通过__init__执行对内存空间数据的填充(对象属性的初始化)。__new__方法的返回值是创建好的Python对象(的引用),而__init__方法的第一个参数就是这个对象(的引用),所以在__init__中可以完成对对象的初始化操作。__new__是类方法,它的第一个参数是类,__init__是对象方法,它的第一个参数是对象。
21.猴子补丁
猴子补丁(Monkey Patching)是指在运行时动态修改或扩展现有的代码,通常是在不修改原始代码的情况下向已有的类、模块或对象添加、修改或删除方法、属性或功能。
猴子补丁的概念来源于动态语言的灵活性,允许开发者在运行时修改代码的行为。在Python中,由于其动态特性,猴子补丁是一种常见的技术。
猴子补丁的应用场景包括但不限于:
修改第三方库的行为:当需要在使用第三方库时,发现其功能不满足需求或有bug时,可以通过猴子补丁来修改或扩展其功能。
临时性的修复:当遇到某个类或模块的bug时,可以通过猴子补丁在不修改源代码的情况下进行临时修复,避免等待官方的修复版本。
动态扩展:在运行时,可以通过猴子补丁向已有的类或对象添加新的方法或属性,以满足特定的需求。
需要注意的是,猴子补丁虽然灵活,但也可能导致代码的可读性和维护性下降,因为它会改变代码的预期行为。因此,在使用猴子补丁时应谨慎,并确保清晰地记录和理解代码的变化。
# 定义一个类
class MyClass:
def my_method(self):
print("原始方法")
# 创建实例并调用原始方法
obj = MyClass()
obj.my_method() # 输出:原始方法
# 定义一个新的方法
def new_method(self):
print("新方法")
# 动态替换原始方法为新方法
MyClass.my_method = new_method
# 再次调用方法
obj.my_method() # 输出:新方法
22. 多进程,多线程,多协程
cpu(计算)密集型计算:io在很短时间内就可以完成,cpu需要大量的计算和处理,特点是cpu占用率相当高,例如:压缩和解压缩,加密解密,正则表达式搜索
io密集型计算:cpu等io(硬盘内存)的读写操作,cpu占用率较低;例如:文件处理程序,网络爬虫程序,读写数据库程序
多进程(multiprocessing)
优点:可以利用多核cpu并行运算
缺点:占用资源最多,可启动数目比线程少
适用于:CPU密集型计算
多线程(threading)
优点:相比进程,更轻量级,占用资源少
缺点:相比进程:多线程只能并发执行,不能利用多cpu(GIL);相比协程:启动数目有限制,占用内存资源,有线程切换开销
适用于:io密集型计算,同时运行的任务数目要求不多
多协程Coroutine(asyncio)
优点:内存开销最少,启动协程数量最多
缺点:支持的库限制(aiohttp/requests)代码实现复杂
适用于:io密集型,需要超多任务运行,但有现成库支持的场景
线程是操作系统分配CPU的基本单位,进程是操作系统分配内存的基本单位。通常我们运行的程序会包含一个或多个进程,而每个进程中又包含一个或多个线程。多线程的优点在于多个线程可以共享进程的内存空间,所以线程间的通信非常容易实现;但是如果使用官方的CPython解释器,多线程受制于GIL(全局解释器锁),并不能利用CPU的多核特性,这是一个很大的问题。使用多进程可以充分利用CPU的多核特性,但是进程间通信相对比较麻烦,需要使用IPC机制(管道、套接字等)。
多线程适合那些会花费大量时间在I/O操作上,但没有太多并行计算需求且不需占用太多内存的I/O密集型应用。多进程适合执行计算密集型任务(如:视频编码解码、数据处理、科学计算等)、可以分解为多个并行子任务并能合并子任务执行结果的任务以及在内存使用方面没有任何限制且不强依赖于I/O操作的任务。
扩展:Python中实现并发编程通常有多线程、多进程和异步编程三种选择。异步编程实现了协作式并发,通过多个相互协作的子程序的用户态切换,实现对CPU的高效利用,这种方式也是非常适合I/O密集型应用的。
23. 如何读取大文件,例如内存只有4G,如何读取一个大小为8G的文件
4G内存要一次性的加载大小为8G的文件是不现实的,遇到这种情况必须要考虑多次读取和分批次处理。在Python中读取文件可以先通过open函数获取文件对象,在读取文件时,可以通过read方法的size参数指定读取的大小,也可以通过seek方法的offset参数指定读取的位置,这样就可以控制单次读取数据的字节数和总字节数。除此之外,可以使用内置函数iter将文件对象处理成迭代器对象,每次只读取少量的数据进行处理,代码大致写法如下所示。
扩展:外部排序跟上述的情况非常类似,由于处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。“排序-归并算法”就是一种常用的外部排序策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件,然后在归并阶段将这些临时文件组合为一个大的有序文件,这个大的有序文件就是排序的结果。
with open('...', 'rb') as file:
for data in iter(lambda: file.read(2097152), b''):
pass
24.举例说明什么情况下会出现KeyError、TypeError、ValueError
变量a是一个字典,执行int(a[‘x’])这个操作就有可能引发上述三种类型的异常。如果字典中没有键x,会引发KeyError;如果键x对应的值不是str、float、int、bool以及bytes-like类型,在调用int函数构造int类型的对象时,会引发TypeError;如果a[x]是一个字符串或者字节串,而对应的内容又无法处理成int时,将引发ValueError。
25.说一下你对Python中模块和包的理解
每个Python文件就是一个模块,而保存这些文件的文件夹就是一个包,但是这个作为Python包的文件夹必须要有一个名为__init__.py的文件,否则无法导入这个包。通常一个文件夹下还可以有子文件夹,这也就意味着一个包下还可以有子包,子包中的__init__.py并不是必须的。模块和包解决了Python中命名冲突的问题,不同的包下可以有同名的模块,不同的模块下可以有同名的变量、函数或类。在Python中可以使用import或from … import …来导入包和模块,在导入的时候还可以使用as关键字对包、模块、类、函数、变量等进行别名,从而彻底解决编程中尤其是多人协作团队开发时的命名冲突问题。
26.字典排序
# 字典计数
s='hello tank tank say hello sb sb'
l=s.split()
dic={}
for item in l:
if item in dic:
dic[item]+=1
else:
dic[item]=1
print(dic)
# 字典排序
prices = {
'AAPL': 191.88,
'GOOG': 1186.96,
'IBM': 149.24,
'ORCL': 48.44,
'ACN': 166.89,
'FB': 208.09,
'SYMC': 21.29
}
sorted(prices,key = lambda x:prices[x],reverse=True)
27.namedtuple,deque,Counter,OrderedDict ,defaultdict ,UserDict
namedtuple是一个函数,用来创建一个带有字段名的元组子类;
deque是一个双端队列,可以在两端进行高效地添加和删除元素;
Counter是一个计数器,用来统计可迭代对象中元素的数量;
OrderedDict是一个有序字典,可以记住元素的插入顺序;
defaultdict是一个带有默认值的字典,当访问不存在的键时,会返回默认值;
UserDict是一个可以用于自定义字典类的基类。它提供了一些方便的方法来操作字典数据。
28.写一个函数实现字符串反转,尽可能写出你知道的所有方法
# 反向切片
def reverse_string(content):
return content[::-1]
# 反转拼接
def reverse_string(content):
return ''.join(reversed(content))
# 递归调用
def reverse_string(content):
if len(content) <= 1:
return content
return reverse_string(content[1:]) + content[0]
# 双端队列
from collections import deque
def reverse_string(content):
q = deque()
q.extendleft(content)
return ''.join(q)
# 反向组装
from io import StringIO
def reverse_string(content):
buffer = StringIO()
for i in range(len(content) - 1, -1, -1):
buffer.write(content[i])
return buffer.getvalue()
# 半截交换
def reverse_string(content):
return ''.join([content[i] for i in range(len(content) - 1, -1, -1)])
# 对位交换
def reverse_string(content):
length, content= len(content), list(content)
for i, j in zip(range(length // 2), range(length - 1, length // 2 - 1, -1)):
content[i], content[j] = content[j], content[i]
return ''.join(content)
29.列表中有1000000个元素,取值范围是[1000, 10000),设计一个函数找出列表中的重复元素。
def find_dup(items: list):
dups = [0] * 9000
for item in items:
dups[item - 1000] += 1
for idx, val in enumerate(dups):
if val > 1:
yield idx + 1000
30.有一个通过网络获取数据的函数(可能会因为网络原因出现异常),写一个装饰器让这个函数在出现指定异常时可以重试指定的次数,并在每次重试之前随机延迟一段时间,最长延迟时间可以通过参数进行控制。
# 方法一
from functools import wraps
from random import random
from time import sleep
def retry(*, retry_times=3, max_wait_secs=5, errors=(Exception, )):
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(retry_times):
try:
return func(*args, **kwargs)
except errors:
sleep(random() * max_wait_secs)
return None
return wrapper
return decorate
# 方法二
from functools import wraps
from random import random
from time import sleep
class Retry(object):
def __init__(self, *, retry_times=3, max_wait_secs=5, errors=(Exception, )):
self.retry_times = retry_times
self.max_wait_secs = max_wait_secs
self.errors = errors
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(self.retry_times):
try:
return func(*args, **kwargs)
except self.errors:
sleep(random() * self.max_wait_secs)
return None
return wrapper
31.写一个函数,传入的参数是一个列表(列表中的元素可能也是一个列表),返回该列表最大的嵌套深度。例如:列表[1, 2, 3]的嵌套深度为1,列表[[1], [2, [3]]]的嵌套深度为3
def list_depth(items):
if isinstance(items, list):
max_depth = 1
for item in items:
max_depth = max(list_depth(item) + 1, max_depth)
return max_depth
return 0
32.写一个函数,传入一个有若干个整数的列表,该列表中某个元素出现的次数超过了50%,返回这个元素
def more_than_half(items):
temp, times = None, 0
for item in items:
if times == 0:
temp = item
times += 1
else:
if item == temp:
times += 1
else:
times -= 1
return temp
33.random的用法
random.random()函数可以生成[0.0, 1.0)之间的随机浮点数。
random.uniform(a, b)函数可以生成[a, b]或[b, a]之间的随机浮点数。
random.randint(a, b)函数可以生成[a, b]或[b, a]之间的随机整数。
random.shuffle(x)函数可以实现对序列x的原地随机乱序。
random.choice(seq)函数可以从非空序列中取出一个随机元素。
random.choices(population, weights=None, *, cum_weights=None, k=1)函数可以从总体中随机抽取(有放回抽样)出容量为k的样本并返回样本的列表,可以通过参数指定个体的权重,如果没有指定权重,个体被选中的概率均等。
random.sample(population, k)函数可以从总体中随机抽取(无放回抽样)出容量为k的样本并返回样本的列表。
34.实现字符串替换
# 方法一
```python
message = 'hello, world!'
print(message.replace('o', 'O').replace('l', 'L').replace('he', 'HE'))
# 方法二
import re
message = 'hello, world!'
pattern = re.compile('[aeiou]')
print(pattern.sub('#', message))
35.逆波兰表达式
import operator
class Stack:
"""栈(FILO)"""
def __init__(self):
self.elems = []
def push(self, elem):
"""入栈"""
self.elems.append(elem)
def pop(self):
"""出栈"""
return self.elems.pop()
@property
def is_empty(self):
"""检查栈是否为空"""
return len(self.elems) == 0
def eval_suffix(expr):
"""逆波兰表达式求值"""
operators = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv
}
stack = Stack()
for item in expr.split():
if item.isdigit():
stack.push(float(item))
else:
num2 = stack.pop()
num1 = stack.pop()
stack.push(operators[item](num1, num2))
return stack.pop()
36.函数参数*arg和kwargs分别代表什么**
Python中,函数的参数分为位置参数、可变参数、关键字参数、命名关键字参数。args代表可变参数,可以接收0个或任意多个参数,当不确定调用者会传入多少个位置参数时,就可以使用可变参数,它会将传入的参数打包成一个元组。**kwargs代表关键字参数,可以接收用参数名=参数值的方式传入的参数,传入的参数的会打包成一个字典。定义函数时如果同时使用args和**kwargs,那么函数可以接收任意参数。
37.什么是鸭子类型(duck typing)
鸭子类型是动态类型语言判断一个对象是不是某种类型时使用的方法,也叫做鸭子判定法。简单的说,鸭子类型是指判断一只鸟是不是鸭子,我们只关心它游泳像不像鸭子、叫起来像不像鸭子、走路像不像鸭子就足够了。换言之,如果对象的行为跟我们的预期是一致的(能够接受某些消息),我们就认定它是某种类型的对象。
在Python语言中,有很多bytes-like对象(如:bytes、bytearray、array.array、memoryview)、file-like对象(如:StringIO、BytesIO、GzipFile、socket)、path-like对象(如:str、bytes),其中file-like对象都能支持read和write操作,可以像文件一样读写,这就是所谓的对象有鸭子的行为就可以判定为鸭子的判定方法。再比如Python中列表的extend方法,它需要的参数并不一定要是列表,只要是可迭代对象就没有问题。
说明:动态语言的鸭子类型使得设计模式的应用被大大简化。
38.说一下Python中变量的作用域
Python中有四种作用域,分别是局部作用域(Local)、嵌套作用域(Embedded)、全局作用域(Global)、内置作用域(Built-in),搜索一个标识符时,会按照LEGB的顺序进行搜索,如果所有的作用域中都没有找到这个标识符,就会引发NameError异常。
39.对闭包的理解。
闭包是支持一等函数的编程语言(Python、JavaScript等)中实现词法绑定的一种技术。当捕捉闭包的时候,它的自由变量(在函数外部定义但在函数内部使用的变量)会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。简单的说,可以将闭包理解为能够读取其他函数内部变量的函数。正在情况下,函数的局部变量在函数调用结束之后就结束了生命周期,但是闭包使得局部变量的生命周期得到了延展。使用闭包的时候需要注意,闭包会使得函数中创建的对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用。
40.Python中的魔术方法
41.用过Python标准库中的模块
42.父子类
class Parent:
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
‘’‘
1 1 1
1 2 1
3 2 3
’‘’
43.生成式
items = [1, 2, 3, 4]
salary_dict = {'nick': 3000,'jason': 100000,'tank': 5000,'sean': 2000}
def func(k):
return salary_dict[k]
print([i for i in items if i > 2])
print([i for i in items if i % 2])
print([(x, y) for x, y in zip('abcd', (1, 2, 3, 4, 5))])
print({x: f'item{x ** 2}' for x in (2, 4, 6)})
print(len({x for x in 'hello world' if x not in 'abcdefg'}))
print(f"max(salary_dict, key=lambda name: salary_dict[name]): {max(salary_dict, key=lambda name: salary_dict[name])}") # max dict lambda
print(f"max(salary_dict, key=func()): {max(salary_dict, key=func)}") # max dict
print(f"sorted(items,reverse=True): {sorted(items,reverse=True)}") # sorted list
print(f"sorted(salary_dict, key=lambda name: salary_dict[name]): {sorted(salary_dict, key=lambda name: salary_dict[name])}") # sorted lambda dict
res = map(lambda name: f"{name} sb", items) # map lambda list
filter_res = filter(lambda name: name.endswith('sb'), items) # filter lambda list
‘’‘
[3, 4]
[1, 3]
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
{2: 'item4', 4: 'item16', 6: 'item36'}
6
’‘’
44.写一个函数统计传入的列表中每个数字出现的次数并返回对应的字典
# 方法一
def count_letters(items):
result = {}
for item in items:
if isinstance(item, (int, float)):
result[item] = result.get(item, 0) + 1
return result
# 方法二
from collections import Counter
def count_letters(items):
counter = Counter(items)
return {key: value for key, value in counter.items() \
if isinstance(key, (int, float))}
45.使用Python代码实现遍历一个文件夹的操作
# f.read(),f.write(),f.readline(),f.readlines() f.flush() f.seek() f.tell() f.truncate()
# r只读 (默认),w清空只写,a追加写入,t 文本模式(默认),b 二进制模式,rt: 可读、可写,wt: 可写、可读,at: 可追加、可读
import os
g = os.walk('/Users/Hao/Downloads/')
for path, dir_list, file_list in g:
for dir_name in dir_list:
print(os.path.join(path, dir_name))
for file_name in file_list:
print(os.path.join(path, file_name))
46. *args || **kwargs || lambda
*args 用来将参数打包成tuple给函数体调用
**kwargs 打包关键字参数成dict给函数体调用
lambda函数有如下特性:
lambda函数是匿名的:所谓匿名函数,通俗地说就是没有名字的函数。lambda函数没有名字。
lambda函数有输入和输出:输入是传入到参数列表argument_list的值,输出是根据表达式expression计算得到的值。
lambda函数一般功能简单:单行expression决定了lambda函数不可能完成复杂的逻辑,只能完成非常简单的功能。
由于其实现的功能一目了然,甚至不需要专门的名字来说明。下面是一些lambda函数示例:
lambda x, y: xy;函数输入是x和y,输出是它们的积xy
lambda:None;函数没有输入参数,输出是None
lambda *args: sum(args); 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
lambda **kwargs: 1;输入是任意键值对参数,输出是1
47.删除重复元素
list(set(lists))
import numpy as np
lists = [1,1,2,3,4,6,9,6,2,2]
lists = np.unique(lists)
lists.sort()
t = lists[-1]
for i in range(len(lists)-2,-1,-1):
if t == lists[i]:
# del lists[i]
lists.remove(lists[i])
else:
t = lists[i]
48.局部变量优先于全局变量使用
a = 100
def divid(a, b):
global a #局部变量
shang = a // b
yushu = a % b
return shang,yushu
sh, ys = divide(3, 4)
50.文件处理xls,sql
# xls
import xlwt
workbook = xlwt.Workbook(encoding='utf-8')
worksheet = workbook.add_sheet('sheet1')
for i in range(0, 9):
for j in range(0, i+1):
worksheet.write(i, j, "%d * %d = %d"%(i+1, j+1, (i+1)*(j+1)))
# worksheet.write(0, 0, 'hello')
workbook.save('student.xls')
# sql
import sqlite3
conn = sqlite3.connect("test.db")
print("opened database successfully")
c = conn.cursor()
sql = "create table company(id int primary key not null , name text not null, age int not null, address char(50), salary real)"
c.execute(sql)
conn.commit()
conn.close()
51.flask
from flask import Flask, render_template, request
import datetime
app = Flask(__name__)
# @app.route('/')
# def index(): # put application's code here
# return render_template("index.html")
@app.route('/index')
def hello_world(): # put application's code here
return 'Hello hh'
@app.route('/index/<name>')
def welcome(name): # put application's code here
return '你好,可爱的 %s' % name
@app.route('/')
def index1():
time = datetime.date.today()
name = ['校长', '小王', '小李']
task = {"renew": "dasaoweisheng", "time": "3h"}
return render_template("index.html", var=time, list=name, task=task)
@app.route('/test/register')
def register():
return render_template("test/register.html")
@app.route('/result', methods=['POST', "GET"])
def result():
if request.method == 'POST':
result = request.form
return render_template("test/result.html", result=result)
if __name__ == '__main__':
app.run(debug=True)
wsgi/werkzeug
Web服务器网关接口,Web Server Gateway Interface
是一种协议、一种规定,遵守WSGI协议能够让web服务器和框架之间解耦,可以混合搭配服务器和框架,互相兼容。
import socket
import sys
from io import StringIO
class WSGIServer(object):
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
request_queue_size = 1
def __init__(self, server_address):
# Create a listening socket
self.listen_socket = listen_socket = socket.socket(
self.address_family,
self.socket_type
)
# Allow to reuse the same address
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind
listen_socket.bind(server_address)
# Activate
listen_socket.listen(self.request_queue_size)
# Get server host name and port
host, port = self.listen_socket.getsockname()[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port
# Return headers set by Web framework/Web application
self.headers_set = []
def set_app(self, application):
self.application = application
def serve_forever(self):
listen_socket = self.listen_socket
while True:
# New client connection
self.client_connection, client_address = listen_socket.accept()
# Handle one request and close the client connection. Then
# loop over to wait for another client connection
self.handle_one_request()
def handle_one_request(self):
self.request_data = request_data = self.client_connection.recv(1024)
# Print formatted request data a la 'curl -v'
print(''.join(
'< {line}\n'.format(line=line)
for line in request_data.splitlines()
))
self.parse_request(request_data)
# Construct environment dictionary using request data
env = self.get_environ()
# It's time to call our application callable and get
# back a result that will become HTTP response body
result = self.application(env, self.start_response)
# Construct a response and send it back to the client
self.finish_response(result)
def parse_request(self, text):
request_line = text.splitlines()[0]
request_line = request_line.rstrip('\r\n')
# Break down the request line into components
(self.request_method, # GET
self.path, # /hello
self.request_version # HTTP/1.1
) = request_line.split()
def get_environ(self):
env = {}
# The following code snippet does not follow PEP8 conventions
# but it's formatted the way it is for demonstration purposes
# to emphasize the required variables and their values
#
# Required WSGI variables
env['wsgi.version'] = (1, 0)
env['wsgi.url_scheme'] = 'http'
env['wsgi.input'] = StringIO.StringIO(self.request_data)
env['wsgi.errors'] = sys.stderr
env['wsgi.multithread'] = False
env['wsgi.multiprocess'] = False
env['wsgi.run_once'] = False
# Required CGI variables
env['REQUEST_METHOD'] = self.request_method # GET
env['PATH_INFO'] = self.path # /hello
env['SERVER_NAME'] = self.server_name # localhost
env['SERVER_PORT'] = str(self.server_port) # 8888
return env
def start_response(self, status, response_headers, exc_info=None):
# Add necessary server headers
server_headers = [
('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),
('Server', 'WSGIServer 0.2'),
]
self.headers_set = [status, response_headers + server_headers]
# To adhere to WSGI specification the start_response must return
# a 'write' callable. We simplicity's sake we'll ignore that detail
# for now.
# return self.finish_response
def finish_response(self, result):
try:
status, response_headers = self.headers_set
response = 'HTTP/1.1 {status}\r\n'.format(status=status)
for header in response_headers:
response += '{0}: {1}\r\n'.format(*header)
response += '\r\n'
for data in result:
response += data
# Print formatted response data a la 'curl -v'
print(''.join(
'> {line}\n'.format(line=line)
for line in response.splitlines()
))
self.client_connection.sendall(response)
finally:
self.client_connection.close()
SERVER_ADDRESS = (HOST, PORT) = 'localhost', 8080
def make_server(server_address, application):
server = WSGIServer(server_address)
server.set_app(application)
return server
if __name__ == '__main__':
httpd = make_server(SERVER_ADDRESS, application)
print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
httpd.serve_forever()
from flask import Flask
app = Flask(__name__)
app.run("0.0.0.0",9527) #监听地址,监听端口
2.Response
return “字符串” -->httpresponse
return render_template(‘html文件’,变量=‘字符串’) -->默认存储位置是remplates,返回模板,依赖markupsafe包
return redirect(“/login”) -->重定向 在响应头中加入-location:url地址,然后跳转
特殊的Response
return send_file(“文件名称或者文件路径+文件名称”) -->打开文件,返回文件你内容,自动识别文件类型,在响应头中加入content-Type:文件类型
return jsonify({k:v}) -->返回标准格式的json字符串,在响应投中加入content-ype:appcation/json在Flask 1.1.1版本中,直接返回dict时,本质上是在运行jsonify;在Flask1.1.1中,可以直接返回字典,flask默认先把字典序列化之后再返回,在content-type中加入application/json
3.Flask Request
request.from 获取FormData中的数据(类似Djnago中的request.POST)to_dict() 转化为类似字典类型,可以通过get和[key]取值,[key],当key不存在时,出现KeyError错误
request.args 获取url中的数据(类似Djnago中的request.GET)to_dict() 转化为类似字典类型,可以通过get和[key]取值,[key],**当key不存在时,出现KeyError错误
request.files 获取Fome中上传文件:
request.json如果请求中存在 Content-Type:application/json ,那么请求体中的数据 被序列化到 request.json 中,并且以 以字典的形式存放
request.data如果请求中 存在Content-Type 中不包含 Form 或 FormData ,保留请求体中的原始数据,是base类型 b" ".存放
request.method #请求方法
request.host #主机位: 127.0.0.1:5000
request.url #完整的url,包括参数
request.path #请求路径,路由地址
request.cookies #字典获取浏览器请求时带上的cookie
request.files # request文件,返回filestorage,.save保存文件
request.heads 获取请求头
4.Flask session
交由客户端保管机制,客户端保存的是一串加密字符串,保存在服务器的内存中, 1.登陆成功后设置session,Flask根据密钥和要设置的session键值对经过位运算,生成session,保存到内存中,需要的话就通过get获取,不需要关闭时删除,并在浏览器中设置键值对–session:string ; 2.在发出请求时,根据浏览器中的–session:string和Flask中secret_key,反序列化就得到了session键值对
1.在Flask中设置密钥
app.secret_key = "@#^%&*&@^&%^#*@"
或者:
app.config['SECRET_KEY'] = "@#^%&*&@^&%^#*@"
2.设置session
session["user"]="123"
3.获取session
session.get('user')
5.flask路由
from flask import Flask
app = Flask(__name__)
@app.route('/',methods=("POST","GET"),endpoint='别名',defaults={"count":20} )
def home(count):
count = request.args.get('count',count) #指定页数就优先,如果没有取默认值
app.run()
6.动态参数路由
可以分页,获取文件,解决分类,解决正则路由问题
from flask import Flask
app = Flask(__name__)
@app.route('/home/<page>') #page默认类型是字符串接收
def home(page): # 需要传参接收
pass
@app.route('/home/<page>_<id>_<id2>') #默认类型是字符串接收
def home(page,id,id2): # 需要传参接收
pass
app.run()
7.Flask初始化配置
from flask import Flask
app = Flask( __name__,
template_folder = 'templates', # 更改母版存放路径,默认值是templates ##重要!!!
static_folder = 'static', # 指定静态文件保存目录,默认值是static "家" ##重要!!! static_url_path = "/static", # 静态文件访问路径,默认值是 /+static_folder "回家的路" ##重要!!! )
#原理
@app.route('/<filename>', )
def func(filename):
filepath = os.path.join('img', filename) # img就是家
return send_file(filepath)
# filepath就是访问路径
if __name__ == '__main__':
app.run()
8.Flask实例配置
from flask import Flask
app = Flask( __name__, template_folder = 'templates', # 更改母版存放路径,默认值是templates ##重要!!! static_folder = 'static', # 指定静态文件保存目录,默认值是static "家" ##重要!!! static_url_path = "/static", # 静态文件访问路径,默认值是 /+static_folder "回家的路" ##重要!!! )
# 实例化配置
app.debug = True # 修改代码自动重启
app.secret_key = '$%^^$' #设置session值需要改密匙
app.session_cookie_name = 'session' # 设置的session名称 ,默认是session app.permanent_session_lifetime= # session生命周期,以秒计时,默认31天
# 另外一种更改配置方式
app.config['DEBUG']= True # 这种方式速度更快
#app.config Flask配置
#app.defalut_config flask默认配置
if __name__ == '__main__':
app.run()
# settinigs.py文件代码
class DebugConfig(object):
"""线上开发环境"""
DEBUG = True
SECRET_KEY = "#$%^&*($#$%^&*%$#$%^&*^%$#$%" PERMANENT_SESSION_LIFETIME = 3600
SESSION_COOKIE_NAME = "I am Not Session"
class TestConfig(object): """测试环境"""
TESTING = True
SECRET_KEY = hashlib.md5(f"{time.time()}#$%^&*($#$%^&*%$#$%^&*^%$#$%{time.time()}".encode("utf8")).hexdigest()
PERMANENT_SESSION_LIFETIME = 360000
SESSION_COOKIE_NAME = "#$%^&*($#$%^&*%$#$%^&*^%$#$%" # session名字 #配置生效
1.导入配置文件 from settings import DebugConfig,TestConfig
2.环境生效 app.config.from_object(DebugConfig) # 线上环境 app.config.from_object(TestConfig) # test环境,需要的时候只需要启用DebugConfig,TestConfig其中一条
9.flask蓝图 blueprint
52. 不带括号时,调用的是函数本身,是整个函数体,是一个函数对象,不需要等待函数执行完成; 带括号时,调用的是函数的执行结果,需等待函数执行完毕的结果。
53. numpy常用知识点
import numpy as np
test = np.array([[1,2,3],[4,5,6],[7,8,9])
a = np.array([[1,2,3],[4,5,6],[7,8,9])
print(test.sum(), test.min(), test.max(), test.mean())
print(test.sum(axis=1)) # 行求和
print(test.sum(axis=0)) # 列求和`
print(a*test) # 值求乘积
print(a**2) # 元素值求平方
print(a.dot(test)) # 矩阵相乘
print(test.ravel()) # 矩阵平坦化
print(np.dot(a, test)) # 矩阵相乘
print(np.exp(test)) # 元素e的n次幂
print(np.sqrt(test)) # 元素开根号
print(np.floor(test)) # 元素向下取整
print(np.shape(test)) # 矩阵形状
print(test.shape) # 矩阵形状
test.shape = (9,1)
test.shape = 9,1
test.shape = 1,-1 # 一个元素为-1,另外一个自动生成
test0 = test.reshape((4,2)) # 改变形状
test1 = test # 相当于引用,不开辟空间
print(test.view()) # 浅复制
print(test.copy()) # 深复制,开辟新空间
print(test.ndim) # 查看维度
print(test.size) # 查看元素个数
print(np.zeros((3,4))) # 生成3行4列的值都为0的元素
[[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.]]
print(np.zeros((3,4),dtype=str)) # 生成3行4列的值都为空的元素
[['' '' '' '']['' '' '' ''] ['' '' '' '']]
print(np.ones((3,4)))
[[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]]
print(np.arange(10,30,5)) # 区间内按等差创建矩阵
[10 15 20 25]
print(np.arange(12).reshape(3,4))
[[ 0 1 2 3] [ 4 5 6 7][ 8 9 10 11]]
from numpy import pi
print(np.linspace(0,2*pi,5)) # 区间内按元素个数取值
[0. 1.57079633 3.14159265 4.71238898 6.28318531]
print(test.T) # 矩阵转置
print(np.vstack((a, test))) # 矩阵按行拼接(竖方向)
print(np.hstack((a, test)) # 矩阵按列拼接(行方向)
print(np.vsplit((a, 参数)) # 矩阵分割按行
print(np.hsplit((a, 参数)) # 矩阵分割按列
test[test=="?"] = 0 # 替换矩阵test中为“?”的元素为0
print(test.dtype) # 查询元素类型,array和series中只能存放相同数据类型的数据
test=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]],dtype='str')
test=test.astype(int) # 转换数据类型
# 利用==、<=、>=判断数组或矩阵中是否含有某个值
test0=np.array([5,10,15,20])
print(test0==15) # [False False True False]
print(test0>=15) # [False False True True]
print(test0==15&20) # [False False False False]
a.sort(axis=1)
print(np.sort(a,axis=1)) # 表示每行元素由小到大排序
a.sort(axis=0)
print(np.sort(a,axis=0)) # 表示每列元素由小到大排序
a = np.array([5,6,4])
b = np.argsort(a) # np.argsort(a)从小到大的排序的坐标
b = np.sin(np.arange(12)).reshape(3,4)
ind = b.argmax(axis=0) # [2 0 0 1]
b_max = b[ind,range(b.shape[1])]
print(b.max(axis=0)) #直接输出最大值
a=np.array([1,2,3])
b=np.tile(a,(3,2)) # 沿x轴方向复制2倍,y轴方向复制3倍
[[1 2 3 1 2 3][1 2 3 1 2 3][1 2 3 1 2 3]]
a = 10*np.random((5,5))
a = (a-a.min())/(a.max()-a.min()) # 数据归一化