重点汇总-python常见问题2
1. 什么是PEP8?
PEP8是一个编程规范,内容是一些关于如何让你的程序更具可读性的建议。
2. 什么是pickling和unpickling?
Pickle
模块读入任何Python对象,将它们转换成字符串,然后使用dump
函数将其转储到一个文件中——这个过程叫做pickling
。反之从存储的字符串文件中提取原始Python对象的过程,叫做unpickling
。
Python是如何被解释的?
Python是一种解释性语言,它的源代码可以直接运行。Python解释器会将源代码转换成中间语言,之后再翻译成机器码再执行。
Python是怎样管理内存的?
Python的内存管理是由私有heap
空间管理的。所有的Python对象和数据结构都在一个私有heap
中。程序员没有访问该heap
的权限,只有解释器才能对它进行操作。为Python的heap空间分配内存是由Python的内存管理模块进行的,其核心API会提供一些访问该模块的方法供程序员使用。Python有自带的垃圾回收系统,它回收并释放没有被使用的内存,让它们能够被其他程序使用。
有哪些工具可以帮助debug或做静态分析?
PyChecker是一个静态分析工具,它不仅能报告源代码中的错误,并且会报告错误类型和复杂度。Pylint是检验模块是否达到代码标准的另一个工具。
数组和元组之间的区别是什么?
数组和元组之间的区别:数组内容是可以被修改的,而元组内容是只读的。另外,元组可以被哈希,比如作为字典的关键字。
注: 这里的数组应该就是列表吧?python很少有叫数组array的,待核实
参数按值传递和引用传递是怎样实现的?
Python中的一切都是类,所有的变量都是一个对象的引用。引用的值是由函数确定的,因此无法被改变。但是如果一个对象是可以被修改的,你可以改动对象。
嵌套列表降维
你有一个列表matrix = [[0,1,2,3], [4,5,6,7], [8,9,10,11]]
降维:
flattened = []
for row in matrix:
for i in row:
flattened.append(i)
# 使用列表生成式:
flattened = [i for row in matrix for i in row]
什么是Python的命名空间?
在Python中,所有的名字都存在于一个空间中,它们在该空间中存在和被操作——这就是命名空间。它就好像一个盒子,每一个变量名字都对应装着一个对象。当查询变量的时候,会从该盒子里面寻找相应的对象。
我们常常听到在python中,一切皆对象。是的,python所操作的数据,都是对象(是对象,就意味着他们有自己独有的方法或属性)。数字、字符串、函数、类,甚至是模块。对,模块也是对象。而在对象中,有一个很重要的角色,那就是命名空间。对象被加载到内存后,需要被调用或者说被引用,就需要有一个指针去指向这个对象,这个指针就是命名空间或者说名称空间。它严格来说只是一个指针或者说对一个对象的引用,你通过查找这个指针,就可以引用这个指针所指向的对象(内存地址中的代码片段)。
Python中的unittest是什么?
在Python中,unittest是Python中的单元测试框架。它拥有支持共享搭建、自动测试、在测试中暂停代码、将不同测试迭代成一组,等等的功能。
在Python中什么是slicing?
Slicing是一种在有序的对象类型中(数组,元组,字符串)节选某一段的语法
在Python中什么是构造器?
生成器是实现迭代器的一种机制。它功能的实现依赖于yield表达式,除此之外它跟普通的函数没有两样。
类构造器__init__
下面这段列表切片输出什么?
list = ['a', 'b', 'c', 'd', 'e']
print list[10:]
上面的代码输出 [ ] ,并且不会导致IndexError
错误
跟你想的一样,当取列表元素的时候,如果索引值超过了元素的个数(例如在上面的列表中,取list[10])
将会导致IndexError
错误。但是,取一个列表的切片的时候,如果起始索引超过了元素个数,将不会引起IndexErro
错误,仅返回一个空列表。
这一特性将会导致一些非常难于追踪的bug,因为在运行时根本没有错误产生。
闭包循环,下面会输出什么 ?
def multipliers():
return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]
上面代码的输出是[6, 6, 6, 6]
(不是[0, 2, 4, 6]).
原因是Python的闭包是延迟绑定(late binding)的。这表明在闭包中使用的变量直到内层函数被调用的时候才会被查找。
如何解决上述问题 ?
def multipliers(): # 解法一
for i in range(4): yield lambda x : i * x # 使用Python的生成器(generator)
def multipliers(): # 解法二
return [lambda x, i=i : i * x for i in range(4)] # 创造一个闭包,通过使用一个默认参数来立即绑定它的参数
from functools import partial # 解法三
from operator import mul # 使用functools.partial函数
def multipliers():
return [partial(mul, i) for i in range(4)]
如下:第2,4,6,8行的输出是什么?
1. list = [ [ ] ] * 5
2. list # output?
3. list[0].append(10)
4. list # output?
5. list[1].append(20)
6. list # output?
7. list.append(30)
8. list # output?
输出结果:
[ [], [], [], [], [] ]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]
这里要理解的关键是,list = [ [ ] ] * 5
并没有创建一个包含5个不同列表的列表。创建的这个列表里的5个列表,是对同一个列表的引用(a a list of 5 references to the same list)。理解了这些,你就能更好地理解余下的输出。
In [431]: a = [[0,1]] # 自己测试
In [432]: a = a * 3
In [433]: a
Out[433]: [[0, 1], [0, 1], [0, 1]]
In [434]: a.pop()
Out[434]: [0, 1]
In [435]: a
Out[435]: [[0, 1], [0, 1]]
In [436]: a[0].append(2)
In [437]: a
Out[437]: [[0, 1, 2], [0, 1, 2]]
列表应用
有一个拥有N个元素的列表,用一个列表解析式生成一个新的列表,元素的值同时满足以下条件:
(a) 偶数,以及
(b) 在原列表中,索引为偶数
[x for x in list[::2] if x%2 == 0] # 解法
写一个函数, 输入一个字符串, 返回倒序排列的结果
输入: string_reverse(‘abcdef’)
, 返回: ‘fedcba’
,写出你能想到的多种方法
方法一:
直接使用字符串切片功能逆转字符串
def func1(one_str):
return one_str[::-1]
方法二:
将字符串转换为列表使用reverse函数
def func2(one_str):
one_str_list = list(one_str) # 这样的list()有时候会报错,注意重启一下Ipython就好了,具体原因未知⚠️
one_str_list.reverse() # list.reverse() 用法
return ''.join(one_str_list)
方法三:
新建一个列表,从后往前添加元素
def func3(one_str):
one_list=[]
for i in range(len(one_str)-1,-1,-1): # 这么理解:len()-1后得出 n,再简化为 range(n,-1,-1),表示从 n 到 -1,中间间隔-1,即倒叙排列 i ,
one_list.append(one_str[i]) # 打印出的 i 应该是 n, n-1, n-2......3,2,1,0
return ''.join(one_list)
方法四:
借助于collections模块现成的方法extendleft
def func4(one_str):
deque1=collections.deque(one_str)
deque2=collections.deque()
for one_char in deque1:
deque2.extendleft(one_char)
return ''.join(deque2)
方法五:
递归实现
def func5(one_str):
if len(one_str)<=1:
return one_str
return one_str[-1]+func5(one_str[:-1])
方法六:
借助基本的Swap操作,以中间为基准交换对称位置的字符
import collections
def func6(one_str):
one_str_list=list(one_str)
if len(one_str_list)==0 or len(one_str_list)==1:
return one_str_list
i=0
length=len(one_str_list)
while i < length/2:
one_str_list[i], one_str_list[length-i-1]=one_str_list[length-i-1], one_str_list[i]
i+=1
return ''.join(one_str_list)
主调用函数
def main_func(str_list):
for one_str in str_list:
one_list=[]
one=[]
one_list.append(func1(one_str))
one_list.append(func2(one_str))
one_list.append(func3(one_str))
one_list.append(func4(one_str))
one_list.append(func5(one_str))
one_list.append(func6(one_str))
print '字符串{0}逆序为:'.format(one_str)
print one_list
if __name__ == '__main__':
str_list=['123456','abcdefg','zyxvuw','Together_CZ']
main_func(str_list)</span></strong>
字符串(str)和列表(list)的互相转换
- str >>>list
str1 = "12345"
list1 = list(str1)
print list1
str2 = "123 sjhid dhi"
list2 = str2.split() #or list2 = str2.split(" ")
print list2
str3 = "www.google.com"
list3 = str3.split(".")
print list3
输出:
['1', '2', '3', '4', '5']
['123', 'sjhid', 'dhi']
['www', 'google', 'com']
- list >>>str
str4 = "".join(list3) # .join() 很重要的函数
print str4
str5 = ".".join(list3)
print str5
str6 = " ".join(list3)
print str6
输出:
wwwgooglecom
www.google.com
www google com
有23枚硬币在桌上,10枚正面朝上。假设别人蒙住你的眼睛,而你的手又摸不出硬币的正反面。让你用最好的方法把这些硬币分成两堆,每堆正面朝上的硬币个数相同。
23枚硬币分成10和13枚,假设10枚硬币这一堆有n个正面朝上的硬币,那么13枚那一堆就有10–n枚正面朝上的硬币。问题就变得很简单了,将10枚那一堆全部翻转一遍不就正好也是10–n枚朝上吗。
Python自省
这个也是python彪悍的特性.
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type()
,dir()
,getattr()
,hasattr()
,isinstance()
.
什么是面向切面编程AOP?
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
知乎
面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。 但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。 一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。 AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。
另一种解说:
首先面向切面编程是什么。就指是把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。让人在同一时间只用思考代码逻辑,或者琐碎事务。代码逻辑比如是插入一条数据,那么琐碎事务就包括获取连接和关闭连接,事务开始,事务提交。切面就是指在大堆繁琐事务中的逻辑代码。
python装饰器就是这样的思路