Python

重点汇总-python常见问题2

2017-12-18  本文已影响40人  时间之友

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)的互相转换


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']
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装饰器就是这样的思路

上一篇下一篇

猜你喜欢

热点阅读