简述函数式编程
将公共的功能编写成函数,通过参数的不同来产生不同的结果。
在函数式编程中,函数是基本单位,变量只是一个名称,而不是一个存储单元。
函数式编程使用一系列的函数解决问题。函数仅接受输入并产生输出,不包含任何能影响产生输出的内部状态。除了匿名函数外,Python还使用filter(),map(),reduce(),apply()函数来支持函数式编程。
查看更多
什么是匿名函数?有什么好处?有什么局限性?
匿名函数,也就是lambda函数,通常用在函数体比较简单的函数上。
好处:函数没有名字,不用担心函数名冲突的问题。
局限性:Python对匿名函数的支持有限,所以一些简单的情况下使用匿名函数。
如何捕获异常,常用的异常机制有哪些?
如果我们没有对异常进行任何预防,那么在程序执行的过程中发生异常,就会中断程序,调用python默认的异常处理器,并在终端输出异常信息。
try…except…finally语句:当try语句执行时发生异常,回到try语句层,寻找后面是否有except语句。找到except语句后,会调用这个自定义的异常处理器。except将异常处理完毕后,程序继续往下执行。finally语句表示,无论异常发生与否,finally中的语句都要执行。
raise主动触发异常,终止程序。
assert语句:判断assert后面紧跟的语句是True还是False,如果是True则继续执行后面的代码,如果是False则中断程序,调用默认的异常处理器,同时输出assert语句逗号后面的提示信息。1
2
3
4
5
6
7
8
9
10
11a=1
assert a>1
print(123)
"""
assert condition False则报错
Traceback (most recent call last):
File "E:/PycharmProjects/my/笔试题/assert.py", line 4, in <module>
assert a > 1
AssertionError
"""
with语句:如果with语句或语句块中发生异常,会调用默认的异常处理器处理,但文件还是会正常关闭。
个人感悟:
1、触发异常,一定要带上完整的异常信息:raise MyException(‘Error message’)
2、创建一个异常模块,创建一些异常基类和子类,触发不同的异常
3、除了特殊情况不要,捕捉所有的异常
4、减少try except中代码,只对出现异常的语句进行处理
5、捕捉异常尽量使用as语句
赋值、浅拷贝、深拷贝的区别(Python里面如何拷贝一个对象?)
赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。
浅拷贝:创建一个新的对象,拷贝了原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会改变){1.完全切片方法;2.工厂函数,如list();3.copy模块的copy()函数}
深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){copy模块的deep.deepcopy()函数}
装饰器函数有什么作用?
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
装饰器遵循开放封闭原则:对修改封闭,对扩展开放。
装饰器的的原则:1. 不修改被装饰对象的源代码 2. 不修改被装饰对象的调用方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 补充
# 被装饰后的函数已经是另一个函数,函数名等函数属性会发生变化。
# 使用functools.wraps来保留原函数的属性。
from functools import wraps
def deco(func):
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
def index():
'''哈哈哈哈'''
print('from index')
print(index.__doc__)
简述Python的作用域,以及Python搜索变量的顺序
Python作用域简单说就是一个变量的命名空间。
代码中变量被赋值的位置,就决定了哪些范围的对象可以访问这个变量,这个范围就是变量的作用域。
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域。
Python的变量名解析机制也称为 LEGB 法则:本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)
import导入顺序是什么
Python标准库模块 ————> Python第三方模块 ————> 自定义模块
新式类和旧式类的区别,如何确保使用的都是新式类?
- 新式类都从object继承,旧式类不需要。
- 新式类的MRO(method resolution order 基类搜索顺序)算法采用C3算法广度优先搜索,而旧式类的MRO算法是采用深度优先搜索。
- 新式类相同父类只执行一次构造函数,经典类重复执行多次。
- 旧式类的类名和type无关,x.__class__定义了x的类名,但是type(x)总是返回
。新式类的结果是一致的,且__class__允许被用户覆盖。 - 新式类增加了__slots__内置属性, 可以把实例属性的种类锁定到__slots__规定的范围之中。
- 新式类增加了__getattribute__()方法,每次通过实例访问属性,都会经过__getattribute__函数。
保证都是新式类的方法:
放在类模块代码的最前面写上__metaclass__=type
继承object类,py3默认继承object,都是新式类。
简述__new__和__init__的区别
- 当创建一个新实例时调用__new__,初始化一个实例时用__init__.
- __new__是一个静态方法,而__init__是一个实例方法.
- __new__方法会返回一个创建的实例,而__init__什么都不返回.
- 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
Python垃圾回收机制
Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。
- 引用计数
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。
优点:1. 简单 2.实时性
缺点:1. 维护引用计数消耗资源 2.循环引用 - 标记-清除机制
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。 - 分代技术
分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
Python默认定义了三代对象集合,索引数越大,对象存活时间越长。
Python的内存管理
引用计数机制
Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
引用计数增加的情况:
1,一个对象分配一个新名称
2,将其放入一个容器中(如列表、元组或字典)
引用计数减少的情况:
1,使用del语句对对象别名显示的销毁
2,引用超出作用域或被重新赋值
sys.getrefcount( )函数可以获得对象的当前引用计数
多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。垃圾回收机制
1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。
2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。内存池机制
Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。
3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
28. re模块中math()和search()、findall()的区别
re.match(pattern,string[,flags]),检查string的开头是否与pattern匹配,返回对象,.group查看。
re.search(pattern,string[,flags]),在string搜索pattern的第一个匹配值,返回对象,.group查看。
re.findall(pattern,string[,flags]),匹配所有项,返回一个所有匹配项的集合。
Python的内存泄露和循环引用
内存泄露的情况:
在没有禁用垃圾回收的前提下,
- 对象被另一个生命周期特别长的对象所引用;
- 循环引用中的对象定义了del函数。
使用gc module、objgraph可以定位内存泄露,定位之后,解决很简单。但必须先禁用垃圾回收(调用gc.disable()),防止误差。
详解
Python中的@property有什么作用?如何实现成员变量的只读属性?
@property装饰器就是负责把一个方法变成属性调用,通常用在属性的get方法和set方法,通过设置@property可以实现实例成员变量的直接访问,又保留了参数的检查。另外通过设置get方法而不定义set方法可以实现成员变量的只读属性。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Foo:
def __init__(self, val):
self.__NAME = val # 将所有的数据属性都隐藏起来
def name(self):
return self.__NAME # obj.name访问的是self.__NAME(这也是真实值的存放位置)
def name(self, value):
if not isinstance(value, str): # 在设定值之前进行类型检查
raise TypeError('%s must be str' % value)
self.__NAME = value # 通过类型检查后,将值value存放到真实的位置self.__NAME
def name(self):
raise TypeError('Can not delete')
f = Foo('egon')
print(f.name)
# f.name=10 #抛出异常'TypeError: 10 must be str'
del f.name # 抛出异常'TypeError: Can not delete'
*args and **kwargs 代表什么意思?我们为什么要使用它们?
*args代表任意多个位置参数,args是一个tuple。它会接收任意多个参数并把这些参数作为元组传递给函数。
**kwargs代表的任意多个关键字参数,kwargs是一个dict。允许你使用没有事先定义的参数名,另外,位置参数一定要放在关键字参数的前面。
它们是可变参数,当函数的参数个数不确定时,就可以使用它们。
有用过with statement吗?它的好处是什么?具体如何实现?
with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。比如文件使用后自动关闭、线程中锁的自动获取和释放等。
自定义支持with语句的对象,需要定义__enter__()方法和__exit()__()方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class DummyResource:
def __init__(self, tag):
self.tag = tag
print('Resource [%s]' % tag)
def __enter__(self):
print('[Enter %s]: Allocate resource.' % self.tag)
return self # 可以返回不同的对象
def __exit__(self, exc_type, exc_value, exc_tb):
print('[Exit %s]: Free resource.' % self.tag)
if exc_tb is None:
print('[Exit %s]: Exited without exception.' % self.tag)
else:
print('[Exit %s]: Exited with exception raised.' % self.tag)
return False # 可以省略,缺省的None也是被看做是False
with DummyResource('aa') as f:
print(f)
@staticmethod和@classmethod
Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38def foo(x):
print("executing foo(%s)" % (x))
class A(object):
def foo(self, x):
print("executing foo(%s,%s)" % (self, x))
def class_foo(cls, x):
print("executing class_foo(%s,%s)" % (cls, x))
def static_foo(x):
print("executing static_foo(%s)" % x)
a = A()
print(a.foo)
print(a.class_foo)
print(a.static_foo)
a.foo(1)
a.class_foo(1)
a.static_foo(1)
"""
运行结果:
<bound method A.foo of <__main__.A object at 0x00000000029A0C18>>
<bound method A.class_foo of <class '__main__.A'>>
<function A.static_foo at 0x00000000029969D8>
executing foo(<__main__.A object at 0x00000000029A0C18>,1)
executing class_foo(<class '__main__.A'>,1)
executing static_foo(1)
"""
方法分为绑定方法和静态方法。实例方法和类方法都是绑定方法,分别绑定给对象和类使用,调用时自动传self和cls参数。静态方法不需要自动传参数,对象和类都可以调用。
| \ | 实例方法 | 类方法 | 静态方法 |
|---|---|---|---|
| a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
| A | 不可用 | A.class_foo(x) | A.static_foo(x) |
Python的反射
反射(自省),主要是指程序可以访问、检查和修改本身状态和行为的一种能力。
Python中的反射,通过字符串的形式操作对象相关的属性。
四个实现自省的函数:
- hasattr(object,name):判断object中有没有一个name字符串对应的方法或属性。
- getattr(object, name, default=None):从object中取一个name字符串对应的方法或属性,没有返回default
- setattr(object, y, v):将对象object中的字符串为y的属性设置为v setattr(obj, ‘y’, v) is equivalent to “obj.y=v”’
- delattr(object, y):将对象object中的字符串为y的属性删除 delattr(obj, ‘y’) is equivalent to “del obj.y”
反射的好处:
- 实现可插拔机制——事先将主要的逻辑写好(只定义接口),然后再去实现接口的功能。
- 可以动态导入模块。importlib模块:importlib.import_module(“xxx.xx”)
查看详情
字典推导式
1 | iterable = {"a": 1, "b": 2, "c": 3, "d": 4} |
Python中的单下划线_和双下划线__
1 | class MyClass(): |
__foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突,就是例如__init__(),__del__(),__call__()这些特殊方法
_foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用from module import * 导入,其他方面和公有一样访问;
__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名,它无法直接像公有成员一样随便访问,通过对象名._类名__xxx这样的方式可以访问.
字符串格式化:%和.format
.format在许多方面看起来更便利.
对于%最烦人的是它无法同时传递一个变量和元组.
“hi there %s” % name
如果name恰好是(1,2,3),它将会抛出一个TypeError异常.为了保证它总是正确的,你必须这样做:
“hi there %s” % (name,) # 提供一个单元素的数组而不是一个参数
迭代器和生成器
迭代:就是重复,下一次的重复是基于上一次的结果。
Python对象中有__iter__方法并且返回一个迭代器的称为可迭代对象。
用for循环的有两种数据类型:
- 集合数据类型——如:字符串、列表、元组、字典、集合等;
- generator,包括生成器和带yield的generator function。
可以用for循环的对象称为可迭代对象:Iterable。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
生成器都是Iterator,列表、字典、字符串是可迭代对象,不是迭代器对象,不过可以通过iter()函数获得一个Iterator对象。
迭代器的优点:
- 提供了一种不依赖于索引的取值方式;
- 惰性计算,节省内存。
迭代器的缺点:
- 取值不如按照索引取值方便。
- 一次性的取值。只能取下一个值,不能取前一个值。
- 无法获取长度。
生成器:Python中一边循环一边计算的机制,称为生成器:generator。
将列表生成式中的[]改为()就会得到一个生成器。
如果一个函数定义中包含yield关键字,这个函数就是一个生成器。
yield的功能:
- 与return类似,都可以返回值,但不一样的地方在于yield返回多次值,而return只能返回一次值;
- 为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器;
- 遵循迭代器的取值方式obj.__next__(),触发的函数的执行,函数暂停与再继续的状态都是由yield保存的。
迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
面向切面编程AOP和装饰器
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
Python为什么不支持函数重载?
函数重载主要是为了解决两个问题:
- 可变参数类型;
- 可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
对于情况1,函数功能相同,但是参数类型不同。Python接收任意类型的参数。
对于情况2,函数功能相同,但是参数个数不通。Python使用可变参数(*args、**kwargs)。
所以Python不需要函数重载。
闭包
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
闭包函数:一个内嵌函数包含对外部作用域,而不是对全局作用域的引用,该函数称为闭包函数。
闭包的特点:
- 必须有一个内嵌函数;
- 内嵌函数必须引用外部函数中的变量;
- 外部函数的返回值必须是内部函数。
闭包的意义:返回的函数对象包裹了一层作用域,使得该函数被调用时优先使用自己外层包裹的作用域。
单例模式
使用__new__方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__new__(cls, )
return cls._instance
class MyClass(Singleton):
a = 1
def __init__(self, name):
self.name = name
one = MyClass('egon')
two = MyClass('alex')
print(id(one))
print(id(two))
print(one == two)
print(one is two)装饰器方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23def singleton(cls, *args, **kw):
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return get_instance
class MyClass2:
a = 1
one = MyClass2()
two = MyClass2()
print(id(one)) # 31495472
print(id(two)) # 31495472
print(one == two)
print(one is two)import方法。Python的模块是天然的单例模式。
module_name中定义类,并实例化。被多次导入都是同一个实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# module_name.py
class MySingleton(object):
def foo(self):
print('danli')
my_singleton = MySingleton()
# to use
from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton))
from .module_name import my_singleton
my_singleton.foo()
print(id(my_singleton))使用metaclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
# Python2
# class MyClass:
# __metaclass__ = Singleton
# Python3
class MyClass(metaclass=Singleton):
pass
one = MyClass()
two = MyClass()
print(id(one))
print(id(two))
print(one == two)
print(one is two)
read,readline和readlines
- read 读取整个文件
- readline 读取下一行,使用生成器方法
- readlines 读取整个文件得到一个列表,每一行是一个元素
Python2和Python3的区别
推荐:http://chenqx.github.io/2014/11/10/Key-differences-between-Python-2-7-x-and-Python-3-x/
Python3中的print是一个函数,Python2中的是一个声明 class
Python3的input()得到str;Python2中的input()得到int,要使用row_input得到str
Python3中的for循环变量不会再导致名称空间泄露。
在python2.7中,range的返回值是一个列表;而在python3.x中,返回的是一个range对象。
map()、filter()、 dict.items()在python2.7返回列表,而在3.x中返回迭代器。
Python3中对不可排序类型做比较时会抛出一个类型异常。
简述ASCII、Unicode、utf-8、gbk的关系
ASCII码是最早美国用的标准信息交换码,把所有的字母的大小写,各种符号用二进制来表示,共有256种,加入些拉丁文等字符,1bytes代表一个字符;
Unicode是为了统一世界各国语言的不用,统一用2个bytes代表一个字符,可以表达2**16=65556个,称为万国语言,特点:速度快,但浪费空间,可以用在内存处理中,兼容了utf-8,gbk,ASCII;
utf-8 为了改变Unicode的缺点,规定1个英文字符用1个字节表示,1个中文字符用3个字节表示,特点;节省空间,速度慢,用在硬盘数据传输,网络数据传输,相比硬盘和网络速度,体现不出来;
gbk 是中文的字符编码,用2个字节代表一个字符。
Python2.7 与 3.x的编码问题
2.7:
字符串两种类型:
- str:某种编码(UTF-8、GBK)类型的字符串
- unicode:Unicode类型的字符串 unicode ——> str
3.X:
字符串两种类型:
- bytes:某种编码(UTF-8、GBK)类型的字符串
- str:Unicode类型的字符串 str ——> bytes str.encode(“utf-8”)
decode() 和 encode()
- decode 的作用是将其他编码的字符串转化成Unicode编码
- encode 的作用是将Unicode编码转化成其他编码的字符串
range和xrange
range和xrange的用法完全相同。
range生成的是一个list对象,xrange生成的是一个序列对象,支持惰性计算,和生成器类似。
Python3中的range就是Python2中的xrange。
Python内置数据类型
内置数据类型:数字、字符串、列表、元组、集合、字典
不可变类型:数字、字符串、元组
可变类型:列表、字典、集合
不可变类型一旦定义赋值,就不能改变,只能重新开辟内存空间。可变类型可以在原内存地址上修改值。
list、tuple、dict、set有什么区别?主要用在什么样的场景?并用for语句分别进行遍历。
定义
list:链表,有序的项目, 通过索引进行查找,使用方括号”[]”;
tuple:元组,元组将多样的对象集合到一起,不能修改,通过索引进行查找, 使用括号”()”;
dict:字典,字典是一组键(key)和值(value)的组合,通过键(key)进行查找,没有顺序, 使用大括号”{}”;
set:集合,无序,元素只出现一次, 自动去重,使用”set([])”
应用场景
list, 简单的数据集合,可以使用索引;
tuple, 把一些数据当做一个整体去使用,不能修改;
dict,使用键值和值进行关联的数据;
set,数据只出现一次,只关心数据是否出现, 不关心其位置;
1 | test_list = [1, 2, 3, 4, 4] |
帮助查找python的bug和进行静态的代码分析工具
- PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告
- Pylint是另外一个工具可以进行coding standard检查
单引号、双引号、三引号的区别
单引号和双引号是等效的,换行需要符号(\);
三引号可以直接换行,并且可以包含注释。
Python是什么?
Python是一种解释型语言。与C语言和C的衍生语言不同,Python代码在运行之前不需要编译。其他解释型语言还包括PHP和Ruby。
Python是动态类型语言,指的是在声明变量时,不需要声明变量的类型。你可以直接编写类似x=111和x=”I’m a string”这样的代码,程序不会报错。
Python非常适合面向对象的编程(OOP),因为它支持通过组合(composition)与继承(inheritance)的方式定义类(class)。Python中没有访问说明符(access specifier,类似C++中的public和private)。
在Python语言中,函数是第一类对象(first-class objects)。这指的是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。
Python代码编写快,但是运行速度比编译语言通常要慢。好在Python允许加入基于C语言编写的扩展,因此我们能够优化代码,消除瓶颈,这点通常是可以实现的。numpy就是一个很好地例子,它的运行速度真的非常快,因为很多算术运算其实并不是通过Python实现的。
Python用途非常广泛——网络应用,自动化,科学建模,大数据应用,等等。它也常被用作“胶水语言”,帮助其他语言和组件改善运行状况。
列举Python让代码并行运行的方法
多线程的创建方式
- 使用Thread类直接创建
- 继承threading.Thread类,重写run方法
查看更多
多进程的创建方式
- 使用multiprocessing.Process直接创建
- 继承multiprocessing.Process类,重写run方法
查看更多
写出匹配IP的正则表达式
1 | import re |
简述值传递和引用传递的区别
- 值传递:仅仅传递的是值
- 引用传递:传递的是内存地址,修改后会改变内存地址对应储存的值
谈谈你对线程安全的理解
线程安全是在多线程的环境下,能够保证多个线程同时执行时程序依旧运行正确, 而且要保证对于共享的数据可以由多个线程存取,但是同一时刻只能有一个线程进行存取。多线程环境下解决资源竞争问题的办法是加锁来保证存取操作的唯一性。
什么是死锁?如何预防死锁?
死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
- 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
- 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
- 循环等待条件:若干进程间形成首尾相接循环等待资源的关系
死锁预防是设法至少破坏产生死锁的四个必要条件之一。
系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
哪些情况下 y != x- (x - y)成立
x,y是两个无交集的非空集合
在Python中删除一个文件
1 | import os |
获取Python解释器版本的方法
1 | import platform |
多进程开发中,join和daemon的区别
join([timeout]):阻塞当前上下文环境的进程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
daemon:设置守护进程,随主进程的生命周期。程序直到所有非守护进程结束而退出。
简单描述GIL对Python性能的影响
GIL是一把加在CPython解释器上的全局解释器锁(加在进程上)
影响:无论启多少个线程,有多少个CPU,Python在执行一个进程的时候在同一时刻只允许一个线程运行,所以CPython是利用多核CPU实现多线程的。Python对于计算密集型的任务开多线程的效率不如串行(没有大量切换)。对于IO密集型任务效率还是有显著提升的。
Python性能优化的建议
- 优化算法时间复杂度
- 减少冗余数据
- 合理使用copy和deepcopy
- 使用dict或set查找元素
- 合理使用生成器(generator)和yield
- 优化循环,循环外能做的事不要放循环内
- 优化包含多个判断表达式
- 使用join合并迭代器中的字符串
- 选择合适的格式化字符方式
- 不借助中间变量交换两个变量的值
unix进程间通信方式(IPC)
- 管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
- 命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
- 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
- 消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
- 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
- 内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
- 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
- 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。