1、简述解释型和编译型(翻译型)编程语言
将较为高级的计算机语言L1转化为较为低级的计算机语言L0(计算机实际执行的语言),这一转化过程称为程序翻译,翻译的工具称为编译器。然后交给计算机执行。L1这种就叫编译型(翻译型)编程语言,有C/C 、Object-C、Golang等
先用较为低级的计算机语言L0实现一个程序(解释器),将高级语言L1作为输入,通过该程序转化为较为低级的计算机语言L0。这一过程称为程序解释。L1这种就叫解释型编程语言,Python、Php、Javascript等
- 计算机执行的指令都是L0
- 翻译过程生成新的L0程序,解释过程不生成新的L0程序
- 解释过程由L0编写的解释器去解释L1程序
Java、C#属于翻译 解释型语言,例如Java程序会先编译成JVM字节码,然后再解释成机器码执行。
?2、Python解释器种类以及特点
CPython C语言开发的 使用最广的解释器
- IPython 基于cpython之上的一个交互式计时器 交互方式增强 功能和cpython一样。
- PyPy 目标是执行效率 采用JIT技术 对python代码进行动态编译,提高执行效率。
- JPython 运行在Java上的解释器 直接把python代码编译成Java字节码执行。
- IronPython 运行在微软 .NET 平台上的解释器,把python编译成. NET 的字节码。
?3、请至少列举5个 PEP8 规范(越多越好)
本篇不细讲了!!采用引用其他文章讲解
?4、通过代码实现如下转换
- 二进制转换成十进制:v = “0b1111011”
str(int(x, 2))
- 十进制转换成二进制:v = 18
bin(int(x,10))
- 八进制转换成十进制:v = “011”
str(int(x, 8))
- 十进制转换成八进制:v = 30
oct(int(x,10))
- 十六进制转换成十进制:v = “0x12”
str(int(v, 16))
- 十进制转换成十六进制:v = 87
hex(int(v,10))
进制转化表:
↓ | 2进制 | 8进制 | 10进制 | 16进制 |
---|---|---|---|---|
2进制 | - | bin(int(x, 8)) | bin(int(x, 10)) | bin(int(x, 16)) |
8进制 | oct(int(x, 2)) | - | oct(int(x, 10)) | oct(int(x, 16)) |
10进制 | str(int(x, 2)) | str(int(x, 8)) | - | str(int(x, 16)) |
16进制 | hex(int(x, 2)) | hex(int(x, 8)) | hex(int(x, 10)) | - |
?5、请编写一个函数实现将IP地址转换成一个整数
代码语言:javascript复制# 如 10.3.9.12 转换规则为
# 10 00001010
# 3 00000011
# 9 00001001
# 12 00001100
# 指定位数 分别输出
def format_ip(ip):
new_ip = ''
for i in ip.split('.'):
# 转化为二进制,并补充到8位
i = bin(int(i))[2:].zfill(8)
new_ip = i
return new_ip
print(format_ip('10.3.9.12'))
?6、python递归的最大层数
程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
例如,下列为某人祖先的递归定义:
某人的双亲是他的祖先(基本情况)。某人祖先的双亲同样是某人的祖先(递归步骤)。斐波纳契数列(Fibonacci Sequence),又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21…… I
Python的最大递归层数是可以设置的,默认的在window上的最大递归层数是998
代码语言:javascript复制import sys
# python设置最大递归层数
sys.setrecursionlimit(2000)
def foo(n):
print(n)
n = 1
foo(n)
if __name__ == '__main__':
foo(1) # 可以看到打印到1998
运行结果如下:
?7、Python求结果
代码语言:javascript复制v1 = 1 or 3 # 1
v2 = 1 and 3 # 3
v3 = 0 and 2 and 1 # 0
v4 = 0 and 2 or 1 # 1
v5 = 0 and 2 or 1 or 4 # 1
v6 = 0 or Flase and 1 # False
# 情况一:当值中存在None/空值时
# or:获取不为None或者空值的值
print(None or 1) # 输出:1
print(1 or None) # 输出:1
# and:获取为None或者空值的值
print(None and 1) # 输出:None
print(1 and None) # 输出:None
# 情况二:当值均无空值时
# or:获取前面的值
print(1 or 2) # 输出:1
print(2 or 1) # 输出:2
# and:获取后面的值
print(1 and 2) # 输出:2
print(2 and 1) # 输出:1
# 情况三 0等于False 1等于True 做与或判断
# 注意:1、or与and优先级相同(顺序执行)2、or具有截断效果
?8、Python中ascii、unicode、utf-8、gbk 区别
ascii: 把所有的字母的大小写,各种符号用二进制来表示,1bytes代表一个字符。
Unicode: 为了统一世界各国语言的不同,统一用2个bytes代表一个字符,特点:速度快,但浪费空间。
utf-8: 为了改变Unicode的这种缺点,规定一个英文字符用一个字节表示,一个中文字符用三个字节表示,特点:节省空间,速度慢。
gbk: 是中文的字符编码,用2个字节代表一个字符。
?9、字节码和机器码的区别
机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据。 通常意义上来理解的话,机器码就是计算机可以直接执行,并且执行速度最快的代码。
字节码(Bytecode)是一种包含执行程序、由一序列 op 代码/数据对 组成的二进制文件。字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能成为机器码的中间代码。
通常情况下它是已经经过编译,但与特定机器码无关。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。
字节码主要为了实现特定软件运行和软件环境、与硬件环境无关。字节码的实现方式是通过编译器和虚拟机器。编译器将源码编译成字节码,特定平台上的虚拟机器将字节码转译为可以直接执行的指令。字节码的典型应用为Java bytecode。
字节码在运行时通过JVM(JAVA虚拟机)做一次转换生成机器指令,因此能够更好的跨平台运行。
总结:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
?10、三元运算规则以及应用场景
代码语言:javascript复制res = 值1 if 条件 else 值2
a = 1 if True else 0
print(a)
# 1
?11、举例python2 python3的区别
- print语句没有了,取而代之的是print()函数。 Python 2.6与Python 2.7部分地支持这种形式的print语法。
- unicode
Python 2 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。 现在, 在 Python 3,我们最终有了 Unicode (utf-8) 字符串,以及一个字节类:byte 和 bytearrays。Python3.X 源码文件默认使用utf-8编码
- 除法运算
在python 2.x中/除法就跟我们熟悉的大多数语言,比如Java啊C啊差不多,整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。
在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。
- 异常
在 Python 3 中处理异常也轻微的改变了,在 Python 3 中我们现在使用 as 作为关键词。
捕获异常的语法由 except exc, var 改为 except exc as var。
- 八进制
八进制数必须写成0o777,原来的形式0777不能用了;二进制必须写成0b111。
新增了一个bin()函数用于将一个整数转换成二进制字串。 Python 2.6已经支持这两种语法。
在Python 3.x中,表示八进制字面量的方式只有一种,就是0o1000。
具体细讲仔细看引用文章
?12、用Python用一行代码实现数值交换
代码语言:javascript复制a = 1
b = 2
a, b = b, a
?13、python3和python2中int和long的区别
python3里面没有long类型,只有int类型
python2 long() 函数将数字或字符串转换为一个长整型。
?14、列举布尔值为False的常见值
代码语言:javascript复制0,-0,None,[],(),{},
使用bool()函数测试即可
?15、Python中字符串、列表、元组、字典每个常用的5个方法
字符串:
代码语言:javascript复制index('子串',开始位置,结束位置):有这个子串,返回第一个字符所在位置的下标。如果没有,则报错
find('子串',开始位置,结束位置) 如果子串不存在,返回-1,不报错
isdigit() 判断是否是纯数字
isalpha() 判断是否是纯字母
isalnum() 判断是否是纯数字或字母组成
startswith() 判断是否以某个子串开头
endswith() 判断是否以某个子串结尾
replace(旧的子串,新的子串,替换次数)
strip()去掉首尾空格
split('分割符号')
upper() 全大写
lower() 全小写
title() 单词首字母大写
capitalize() 字符串首字母大写
列表:
代码语言:javascript复制 合并
split()
split() 字符串转化成列表。
join() 列表转化成字符串 ‘分割符号’.join(列表)
isinstance(数据,指定的数据类型) 判断数据是否是指定的数据类型
insert(位置,数据):指定位置添加数据
append() 向列表结尾添加数据
extend() 向列表结尾添加数据(拆开数据)
in not in
remove() 删除指定数据
pop() 不指定下标的话,从结尾删除。都会有一个返回值,表示的是删除的数据
clear() 清空列表
元组:
代码语言:javascript复制add() 添加一个数据 s1.add(数据)
update([])添加多个数据
remove() 删除指定数据,没有则报错
pop() 删除第一个数据
discard() 删除指定数据,没有这个数据,不做任何反应。
字典:
代码语言:javascript复制fromkeys() 创建键值对的值都是相同的列表,要求第一个参数必须是列表,用来书写键名
dict = {}
dict = dict.fromkeys(['aa', 'bb'],10)
# {'aa': 10, 'bb': 10}
get():通过键名查找数据
update():更新。dict1.update(dict2).用2更新1,既能修改又能新增。
keys():返回所有键的列表
values(): 返回所有值的列表
items():返回一个列表,包含键值对对应关系的元组
pop() 删除指定键的数据。
popitem() 删除最后的数据
clear() 清空字典
?16、Python中的lambda 表达式格式及应用场景
Lambda表达式是给函数式接口(FunctionalInterface)的形参或变量赋值用的。
- lambda只是一个表达式,函数体比def简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法:
代码语言:javascript复制**lambda [arg1 [,arg2,.....argn]]:expression
sum = lambda x,y:x y
# 调用sum函数
print(sum(1,2))**
?17、Python中的pass的作用
Python pass 是空语句,是为了保持程序结构的完整性。
pass 不做任何事情,一般用做占位语句。而且pass也不影响前后语句的执行。
?18、*arg和**kwarg作用
代码语言:javascript复制首先,完整地写*args和**kwargs是不必要的,我们可以只写*和**。
*args的使用
通常用在函数定义里,*args能够接收不定量的非关键参数
代码语言:javascript复制def learn_args(v,*args):
print('first parameter',v)
# 值得注意的是这里的args是元组类型
for arg in args:
print('other parameters',arg)
learn_args('G','J','W')
"""
first parameter G
other parameters J
other parameters W
"""
代码语言:javascript复制**kwargs的使用
**kwargs能够接收多个关键词参数
def learn_kwargs(v,**kwargs):
print('first parameter',v)
# 这里的kwargs自动形成字典
print(type(kwargs))
print(kwargs)
learn_kwargs('G',a='1',b=1,c=[])
"""
first parameter G
<class 'dict'>
{'a': '1', 'b': 1, 'c': []}
"""
?19、is和==的区别
is
比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址。
==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()
方法。
[:]
这是一个copy浅拷贝方法
a = [1, 2, 3]
b = a
b is a # True
b == a # True
b = a[:] # 此时b=[1,2,3] 但是被分配了新的内存
b is a # Flase
b == a # True
?20、简述Python的深浅拷贝以及应用场景
- 直接赋值: 其实就是对象的引用(别名)。
- 浅拷贝(copy): 拷贝父对象,不会拷贝对象的内部的子对象。
- 深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
b = a: 赋值引用,a 和 b 都指向同一个对象。
b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
代码语言:javascript复制>#!/usr/bin/python
># -*-coding:utf-8 -*-
>import copy
>a = [1, 2, 3, 4, ['a', 'b']] #原始对象
>b = a #赋值,传对象的引用
>c = copy.copy(a) #对象拷贝,浅拷贝
>d = copy.deepcopy(a) #对象拷贝,深拷贝
>a.append(5) #修改对象a
>a[4].append('c') #修改对象a中的['a', 'b']数组对象
>print( 'a = ', a )
>print( 'b = ', b )
>print( 'c = ', c )
>print( 'd = ', d )
>"""
>('a = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
>('b = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
>('c = ', [1, 2, 3, 4, ['a', 'b', 'c']])
>('d = ', [1, 2, 3, 4, ['a', 'b']])
>"""
如果再不明白的话可以参考这篇文章
?21、Python垃圾回收机制
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。
- Python语言默认采用的垃圾收集机制是『引用计数法
Reference Counting
』,该算法最早George E. Collins在1960的时候首次提出,50年后的今天,该算法依然被很多编程语言使用。 - 『引用计数法』的原理是:每个对象维护一个
ob_ref
字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数ob_ref
加1
,每当该对象的引用失效时计数ob_ref
减1
,一旦对象的引用计数为0,该对象立即被回收,对象占用的内存空间将被释放。 - 它的缺点是需要额外的空间维护引用计数,这个问题是其次的,不过最主要的问题是它不能解决对象的“循环引用”,因此,也有很多语言比如Java并没有采用该算法做来垃圾的收集机制。
标记清除
标记清除(Mark—Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢?
对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对
象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动
对象。根对象就是全局变量、调用栈、寄存器。 mark-sweepg 在上图中,我们把小黑圈视为全局变量,也就是把它作为root
object,从小黑圈出发,对象1可直达,那么它将被标记,对象2、3可间接到达也会被标记,而4和5不可达,那么1、2、3就是活
动对象,4和5是非活动对象会被GC回收。
标记清除算法作为Python的辅助垃圾收集技术主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符
串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除
算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。
分代回收
- 分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
- 新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
- 同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象。
如果不还有不明白的地方可以参考以下文章(建议详读)
?22、Python的可变类型和不可变类型
这里的可变与不可变是指内存中的位置在变量被定义之后是否可以被改变
不可变类型
- int
a = 1
print(id(a)) # 140718582096528
a = 1
print(id(a)) # 140718582096560
- float
- decimal
- complex
- bool
- String
- tuple
- range
- frozenset
- bytes
可变类型
- List
list = []
id(list)
# 2756476232392
list.append(1)
id(list)
# 2756476232392
- dict
- set
- bytearray
- user-defined classes (unless specifically made immutable)
?️23、求结果并解释
代码语言:javascript复制>>> v = dict.fromkeys(['k1','k2'],[])
>>> v['k1'].append(666)
>>> print(v)
{'k1': [666], 'k2': [666]}
>>> v['k1'] = 777
>>> print(v)
{'k1': 777, 'k2': [666]}
fromkeys([‘k1’,‘k2’],[])生成字典时候,第一参数是可迭代对象,第二个是字典的value值,每个key对应的value值是相同的自然这些value值指向的也是同一个内存。而我们知道列表是不可变类型,即一个列表内存位置不会发生改变。在对其中一个key的value列表进行append的时候,所有value值也发生变化。
?24、求结果
代码语言:javascript复制def num():
return [lambda x:i*x for i in range(4)]
print([m(2) for m in num()])
# [6,6,6,6]
这个问题涉及到了python的闭包概念,lambda是num()的内嵌函数,而变量i在lambda中并没有被定义,所以会在嵌套作用域中寻找变量i,而此时i等于for循环中的最大值3。
只有函数、类、模块会产生作用域,代码块不会产生作用域。作用域按照变量的定义位置可以划分为4类:
- Local (函数内部)局部作用域。
- Enclosing (嵌套函数的外层函数内部)嵌套作用域(闭包)。
- Global (模块全局)全局作用域。
- Built-in (内建)内建作用域。
python解释器查找变量时,会按照顺序依次查找局部作用域—>嵌套作用域—>全局作用域—>内建作用域,在任意一个作用域中找到变量则停止查找,所有作用域查找完成没有找到对应的变量,则抛出 NameError: name ‘xxxx’ is not defined的异常。
如果让其输入为[0,2,4,6],则可以将代码中的i设置为局部作用域,且从for中获取i的值。
代码语言:javascript复制def num():
return [lambda x,i=i:i*x for i in range(4)]
print([m(2) for m in num()])
# [0,2,4,6]
?25、举例常见的内置函数
代码语言:javascript复制# 一个可迭代对象的所有元素都为真,则返回True
all([1,2,3]) # True
all([0,1,2]) # False
# 一个可迭代对象中只要有一个元素为真,则返回True
any([1,0,0]) # True
any([0]) # False
# bin hex oct
# callable判断一个对象是否是可以被调用的,即类似于“test()”这样的写法。函数与类皆可以被调用
b = 2
def func():
pass
print(callable(b),callable(func)) # False True
# exec将一串代码字符串,以代码的形式执行
code = '''
n = 0
while n < 10:
print(n)
n =1
'''
exec(code)
?26、filter、map、reduce的作用
reduce在python3中被移除,需要从functools中引用。python3中,filter,map都变为了迭代器
代码语言:javascript复制from functools import reduce
list(map(lambda x:x*x,[0,1,2,3,4,5,6]))
# [0, 1, 4, 9, 16, 25, 36]
reduce(lambda x,y:x y,[0,1,2,3,4,5,6])
# 21
# &是二进制按位与运算,filter把0剔除了
list(filter(lambda x:x&1,[0,1,2,3,4,5,6]))
# [1, 3, 5]
?27、re的match和search区别
re.match(pattern, string[, flags])
从首字母开始开始匹配,string如果包含pattern子串,则匹配成功,返回Match对象,失败则返回None,若要完全匹配,pattern要以$结尾。
re.search(pattern, string[, flags])
若string中包含pattern子串,则返回Match对象,否则返回None,注意,如果string中存在多个pattern子串,只返回第一个。
re.findall(pattern, string[, flags]
)返回string中所有与pattern相匹配的全部字串,返回形式为数组。
?28、什么是正则的贪婪匹配
**贪婪模式:**在整个表达式匹配成功的前提下,尽可能多的匹配。
非贪婪模式:在整个表达式匹配成功的前提下,以最少的匹配字符。
默认是贪婪模式
非贪婪模式只需在匹配pattern中加上?:
表达式ab.*?c 测试数据:abacaxcd,匹配结果:abac
?29、求结果
代码语言:javascript复制[ i % 2 for i in range(10) ]
# [0,1,0,1,0,1,0,1,0,1]
( i % 2 for i in range(10) )
# <generator object <genexpr> at 0x000002239A531930>
?30、求结果
前两题解析见第七题
第三题括号有比较优先级,1是True,0是False
最后一题是涉及到Python链式对比(Chained Comparisons),例如Python中1 < x < 3等于1 < x and x < 3
代码语言:javascript复制1 or 2 # 1
1 and 2 # 2
1 < (2==2) # False
# 1 < 2 and 2 == 2
1 < 2 == 2 # True
?31、def func(a,b=[]) 这种写法有什么坑
参数如果不填的话最好设置为None,而不要这样设置为空列表。因为函数在定义的时候b已经被赋值了,而列表是不可变类型,添加元素存储地址不发生改变,下面例子的func(2)结果中返回的列表显然还是第一次调用时候的列表,列表并没有重置。
代码语言:javascript复制def func(a,b=[]):
b.append(a)
return b
func(1)
func(2)
"""
[1]
[1,2]
"""
?32、如何实现 “1,2,3” 变成 [‘1’,’2’,’3’]
代码语言:javascript复制string = '1,2,3'
string.split(',')
?33、如何实现[‘1’,’2’,’3’]变成[1,2,3]
代码语言:javascript复制num_list = ['1','2','3']
[int(n) for n in num_list]
?34、比较:a = [1,2,3] 和 b = [(1),(2),(3) ] 以及 c = [(1,),(2,),(3,) ] 的区别
代码语言:javascript复制a和b里面的元素是一样的,c里面的元素是元组
a[0] == b[0] # True
?35、如何用一行代码生成[1,4,9,16,25,36,49,64,81,100]
代码语言:javascript复制[ i*i for i in range(1,11)]
?36、一行代码实现删除列表中重复的值
代码语言:javascript复制num_list = [1,2,3,4,5,7,1]
list(set(num_list))
?37、如何在函数中设置一个全局变量
代码语言:javascript复制# global关键字(内部作用域想要对外部作用域的变量进行修改)
# 首先外部要有被变量
a = 1
def func():
global a
a = 'wdnmd'
# 调用之后,global才会起作用
func()
print(a)
"""
wdnmd
"""
?38、logging模块的作用?以及应用场景
日志模块,用来记录用户的行为 或者 代码执行的过程。作用:可以了解程序的运行情况是否正常,在程序出现故障快速定位出错地方以及故障分析。
本篇不细讲了引用本文章
?39、请用代码简答实现stack
栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。
我们来通过list简单实现stack
代码语言:javascript复制class Stack(object):
def __init__(self):
self.stack=[]
# 入栈
def push(self, value):
self.stack.append(value)
# 出栈
def pop(self):
if self.stack:
self.stack.pop()
else:
raise LookupError('stack is empty')
def is_exsit(self):
return bool(self.stack)
# 取出栈中的最新元素
def top(self):
return self.stack[-1]
?40、简述 生成器、迭代器、可迭代对象 以及应用场景
生成器: Python使用生成器对延迟操作提供了支持,所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。
比如我们要循环输出有规律的数组,我们可以计算一次,返回一次,这就是生成器,而不是一次性生成列表。
产生生成器的方法:
1、生成器表达式
代码语言:javascript复制>>> generator_object = (x*x for x in range(10))
>>> generator_object
<generator object <genexpr> at 0x000001A77EDB16D8>
2、生成器函数
和常规函数定义一样,但是返回语句return被yield语句代替了.yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
代码语言:javascript复制>>> def generator_func(n):
... for i in range(n):
... yield i*i
...
>>> generator_func(10)
<generator object generator_func at 0x000001A77EDB19A8>
自动实现迭代器协议:对于生成器,Python会自动实现它的可迭代协议,以便用在可以迭代的地方.所以我们可调用它的next方法,获取下一个元素,并且在没有值可以返回的时候,生成器会自动产生StopIteration异常。
迭代器:
迭代器对象就是实现了iter() 和 next()方法的对象.其中iter()返回迭代器本身,而next()返回容器的下一个元素,在结尾处引发StopInteration异常。
ir=iter(itrable)将一个可迭代对象转化为迭代器,next(ir)获取下迭代器中的下一个值。
可迭代对象就是可以转化为迭代器的对象,比如dict、tuple、list、str等。而且他们都可用作for循环,遵循可迭代协议。
?41、用Python实现一个二分查找的函数
二分查找又称折半查找,优点是比较次数少、查找速度快、平均性能好;其缺点是要求待查表为有序表,且插入删除困难,因此折半查找适用于不经常变动而查找频繁的有序序列。其算法思想是将表中中间位置记录的关键字与要查找的关键字比较,如果两者相等则查找成功,否则利用中间位置将表分成前、后两个子表。如果中间记录的关键字大于查找关键字,则进一步查找前一字表,否则进一步查找后一子表。重复以上过程,直到满足条件则查找成功,否则查找失败。
算法实现:
代码语言:javascript复制def binary_search(array, val):
"""
python二分查找,查找数值在有序列表中的位置,找到后返回在列表中顺序
"""
length = len(array)
low = 0
high = length 1
while (low <= high):
mid = (low high) // 2
if val < array[mid]:
high = mid-1
elif val > array[mid]:
low = mid 1
else:
return mid
return None
if __name__ == "__main__":
array = [1, 2, 4, 6, 7, 9, 10]
print(binary_search(array, 5))
print(binary_search(array, 4))
print(binary_search(array, 1))
print(binary_search(array, 10))
?42、谈谈你对闭包的理解
闭包概念:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。
代码语言:javascript复制def origin(x):
def closure(y):
return x y
return closure
函数origin中返回的是函数closure,而函数closure对orign的x变量进行了引用,这就是闭包。闭包也是装饰器的原理。
?43、os和sys模块的作用
Python 的 os 模块封装了常见的文件和目录操作
下面是部分常见的用法:
方法 | 说明 |
---|---|
os.mkdir | 创建目录 |
os.rmdir | 删除目录 |
os.rename | 重命名 |
os.remove | 删除文件 |
os.getcwd | 获取当前工作路径 |
os.walk | 遍历目录 |
os.path.join | 连接目录与文件名 |
os.path.split | 分割文件名与目录 |
os.path.abspath | 获取绝对路径 |
os.path.dirname | 获取路径 |
os.path.basename | 获取文件名或文件夹名 |
os.path.splitext | 分离文件名与扩展名 |
os.path.isfile | 判断给出的路径是否是一个文件 |
os.path.isdir | 判断给出的路径是否是一个目录 |
“sys”即“system”,“系统”之意。该模块提供了一些接口,用于访问 Python 解释器自身使用和维护的变量,同时模块中还提供了一部分函数,可以与解释器进行比较深度的交互。
下面是部分常见的用法:
方法 | 说明 |
---|---|
sys.argv | 展示调用python提供的命令行参数 |
sys.platform | 运行的平台 |
sys.executable | 解释器对应的绝对路径 |
sys.getsizeof | 作用对象占用的字节数 |
?44、Python面向对象中的继承有什么特点
提高代码的复用程度,避免重复操作
特点:
1.在继承中基类的构造(init()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
2.在调用基类的方法时候,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时候并不需要带上self参数。
3.python总是首先札沼对应类型的方法,如果不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
?45、面向对象深度优先和广度优先是什么
Python的类可以继承多个类,Python的类如果继承了多个类,那么其寻找方法的方式有两种: 当类是经典类时,多继承情况下,会按照深度优先方式查找 (py3) ;当类是新式类时,多继承情况下,会按照广度优先方式查找( py2)。
简单点说就是:经典类是纵向查找,新式类是横向查找。
经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。
在python3中默认全是新式类
?46、是否使用过functools中的函数?其作用是什么
functools用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是可以被当做函数调用的对象就是这个模块的目标。模块提供了许多改写或拓展函数或其他可调用对象的工具,而无需完全重写它们。
functools 模块中提供的主要工具是 partial 类,它可以用来包装一个可调用对象,使其具有默认参数。生成的对象也是一个可调用对象,并且可以把它当做原来的函数。新生成的可调用对象可接受和原有函数完全一样的参数,并且可以在调用时接受额外的位置参数或关键词参数。partial 可以代替 lambda 为函数参数提供默认值,并同时留下一些没有指定默认值的参数。
代码语言:javascript复制from functools import partial
foo = partial(int,base=2) # 重新包装int函数为foo,将二进制转换为10进制
print(foo('11110111'))
>>> 247
?47、列举面向对象中带双下划线的特殊方法,如:new、init
- _xx前置单下划线,私有属性或方法,意思是只有类对象和子类对象自己能访问到这些变量;
- __xx前置双下划线,私有化属性或方法,无法在外部直接访问(名字重整所以访问不到,只能是允许这个类本身进行访问了。连子类也不可以)。
- xx:前后双下划线,系统定义名字(这就是在python中强大的魔法方法),因为变量名__xx__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
- xx_:后置单下划线,用于避免与Python关键词的冲突
__init__是属于Python中的魔法方法。所谓魔法方法,即是Python中内置的、当进行特定操作时,会自动调用的方法,表现为方法名前后有两个下划线。
__new__方法是将对象创建出来的方法。在实际运行中,先走__new__方法,生成对象并返回,后调用__init__方法,将对象的引用传给__init__方法中的self。
?48、如何判断是函数还是方法
代码语言:javascript复制class Capybara():
def eat(self,food='melon'):
pass
small_capy = Capybara()
print(Capybara.eat)
print(small_capy.eat)
>>><function Capybara.eat at 0x0000013BB6436AE8>
>>><bound method Capybara.eat of <__main__.Capybara object at 0x0000013BB6395E80>>
可以看出通过类的方法调用就是函数,通过实例化调用就是方法。
?48、如何判断是函数还是方法?
class Capybara():
代码语言:javascript复制def eat(self,food='melon'):
pass
small_capy = Capybara() print(Capybara.eat) print(small_capy.eat)
<function Capybara.eat at 0x0000013BB6436AE8> <bound method Capybara.eat of <main.Capybara object at 0x0000013BB6395E80>> 可以看出通过类的方法调用就是函数,通过实例化调用就是方法。
?49、静态方法和类方法区别
实例方法只能被实例对象调用,静态方法(由@staticmethod装饰的方法)、类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用。
实例方法,第一个参数必须要默认传实例对象,一般习惯用self。
静态方法,参数没有要求。
类方法,第一个参数必须要默认传类,一般习惯用cls。
代码语言:javascript复制class Foo(object):
"""类三种方法语法形式"""
def instance_method(self):
print("是类{}的实例方法,只能被实例对象调用".format(Foo))
@staticmethod
def static_method():
print("是静态方法")
@classmethod
def class_method(cls):
print("是类方法")
foo = Foo()
foo.instance_method()
foo.static_method()
foo.class_method()
print('----------------')
Foo.static_method()
Foo.class_method()
?50、列举面向对象中的特殊成员以及应用场景
代码语言:javascript复制"""
__doc__描述类的信息
__module__表示当前操作对象在哪个模块
__class__表示当前操作对象的类
__del__构析方法,当对象在内存中被释放时,自动触发执行
__call__如果类中定义了call方法对象后面加括号,触发执行
__dict__类或对象中的所有成员
__str__如果类中定义了str方法,打印对象时,默认输出该方法的返回值
"""
?51、1、2、3、4、5 能组成多少个互不相同且无重复的三位数
实现思路:
1.从列表中依次取出一个数,作为百位
2.从列表中依次取出一个数,作为十位
3.列表中取出剩下的百位。
实现方式有两种,都是三层循环嵌套,第一种是在每次循环中拷贝原列表,pop出元素,不影响列表完整性;第二种在每次循环pop元素,套下一层循环之后,将元素添加回去,同样不影响列表完整性。
这里使用的是方法二
代码语言:javascript复制li = [1, 2, 3, 4, 5]
count = 0
for i in range(len(li)): # 循环找出百位
x = str(li.pop(i))
for j in range(len(li)): # 在列表剩下的数中寻找十位
y = str(li.pop(j))
for k in range(len(li)): # 在列表剩下的数中寻找个位
z = str(li[k]) # 注意这里不能用pop弹出数据,因为pop之后,列表发生了改变,要依次使用列表中剩余的数字
print(x y z, end="|")
count = 1
li.insert(j, int(y)) # 将数字送回列表,方便下一次循环
li.insert(i, int(x))
print('number of unrepeated number is', count)
?52、什么是反射?以及应用场景
放射:通过字符串映射object对象的方法或者属性
hasattr(obj,name_str): 判断objec是否有name_str这个方法或者属性。
getattr(obj,name_str): 获取object对象中与name_str同名的方法或者函数。
setattr(obj,name_str,value): 为object对象设置一个以name_str为名的value方法或者属性。
delattr(obj,name_str): 删除object对象中的name_str方法或者属性。
可以用一个方法方便调用不同的函数,比如在我们做接口自动化的时候,需要通过不同的请求方式,调用不同的函数:
参考本文章