python面试题(四)

1 常用字符串格式化哪几种?

最方便的

print('hello %s and %s' % ('df', 'another df'))

但是,有时候,我们有很多的参数要进行格式化,这个时候,一个一个对应就有点麻烦了,于是就有了第二种,字典形式的。上面那种是tuple形式的。

最好用的

print('hello %(first)s and %(second)s' % {'first': 'df', 'second': 'another df'})

这种字典形式的字符串格式化方法,有一个最大的好处就是,字典这个东西可以和json文件相互转换,所以,当配置文件使用字符串设置的时候,就显得相当方便。

最先进的

print('hello {first} and {second}'.format(first='df', second='another df'))

2 简述 生成器、迭代器、可迭代对象 以及应用场景?

迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

使用迭代器的优点 
对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。 
另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。 
迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了iter()方法对象,就可以使用迭代器访问。

迭代器有两个基本的方法

next方法:返回迭代器的下一个元素

__iter__方法:返回迭代器对象本身

下面用生成斐波那契数列为例子,说明为何用迭代器

#代码1:直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。
def fab(max): 
   n, a, b = 0, 0, 1 
   while n < max: 
       print b 
       a, b = b, a + b 
       n = n + 1
#代码2:代码2满足了可复用性的需求,但是占用了内存空间,最好不要。
def fab(max): 
   L = []
   n, a, b = 0, 0, 1 
   while n < max: 
       L.append(b) 
       a, b = b, a + b 
       n = n + 1
   return L
#代码3:
class Fab(object): 
   def __init__(self, max): 
       self.max = max 
       self.n, self.a, self.b = 0, 0, 1 

   def __iter__(self): 
       return self 

   def next(self): 
       if self.n < self.max: 
           r = self.b 
           self.a, self.b = self.b, self.a + self.b 
           self.n = self.n + 1 
           return r 
       raise StopIteration()

执行

>>> for key in Fabs(5):
   print key

1
1
2
3
5

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数  

使用内建的工厂函数iter(iterable)可以获取迭代器对象:

>>> lst = range(5)
>>> it = iter(lst)
>>> it
<listiterator object at 0x01A63110>

使用next()方法可以访问下一个元素:

>>> it.next()
0
>>> it.next()
1
>>> it.next()
2

事实上,因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下

>>> a = (1, 2, 3, 4)
>>> for key in a:
   print key

1
2
3
4

首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

生成器

带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明) 
可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果

#代码4
def fab(max):
   n, a, b = 0, 0, 1
   while n < max:
       yield b
       a, b = b, a + b
       n = n + 1

执行

>>> for n in fab(5):
   print n

1
1
2
3
5

简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

>>> f = fab(3)
>>> f.next()
1
>>> f.next()
1
>>> f.next()
2
>>> f.next()

Traceback (most recent call last):
 File "<pyshell#62>", line 1, in <module>
   f.next()
StopIteration

Python可迭代对象(Iterable) 

我们已经知道可以对list、tuple、str等类型的数据使⽤for...in...的循环语法从

其中依次拿到数据进⾏使⽤,我们把这样的过程称为遍历,也叫迭代。

但是,是否所有的数据类型都可以放到for...in...的语句中,然后让for...in...

每次从中取出⼀条数据供我们使⽤,即供我们迭代吗?

我们把可以通过for...in...这类语句迭代读取⼀条数据供我们使⽤的对象称之为可迭代对象(Iterable)

两种方法:

1.可迭代对象.__iter__()

2.iter(可迭代对象)

如何判断对象是否可以迭代

可以使用  isinstance()    判断⼀个对象是否是    Iterable   对象:

from    collections    import    Iterable
isinstance([],    Iterable) Out[51]:    True
isinstance({},    Iterable)
True
isinstance('abc',    Iterable)
True
isinstance(mylist,    Iterable)
False
isinstance(100,    Iterable)
False


3 用Python实现一个二分查找的函数。

题目:

输入指定列表和一个待查找的元素,输出元素是否在列表中,若存在则返回下标

思想:

利用二分查找来做,事先需要对列表进行排序,二分查找只对有序表有效

下面是具体的实现:

def binary_search(num_list, x):
  '''
  二分查找
  '''
  num_list=sorted(num_list)
  left, right = 0, len(num_list)
  while left < right:
      mid = int((left + right) / 2)
      if num_list[mid] > x:
          right = mid
      elif num_list[mid] < x:
          left = mid + 1
      else:
          return '待查元素{0}在列表中下标为:{1}'.format(x, mid)
  return  '待查找元素%s不存在指定列表中'%x
if __name__ == '__main__':
  num_list = [34,6,78,9,23,56,177,33,2,6,30,99,83,21,17]
  print(binary_search(num_list, 34))
  print(binary_search(num_list, 177))
  print(binary_search(num_list, 21))
  print(binary_search(num_list, 211))
  print(binary_search(num_list, 985))

结果如下:

待查元素34在列表中下标为:9
待查元素177在列表中下标为:14
待查元素21在列表中下标为:5
待查找元素211不存在指定列表中
待查找元素985不存在指定列表中

4 谈谈你对闭包的理解?

闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。

不同的语言实现闭包的方式不同。Python以函数对象为基础,为闭包这一语法结构提供支持的 (我们在特殊方法与多范式中,已经多次看到Python使用对象来实现一些特殊的语法)。Python一切皆对象,函数这一语法结构也是一个对象。在函数对象中,我们像使用一个普通对象一样使用函数对象,比如更改函数对象的名字,或者将函数对象作为参数进行传递。

下面看一个闭包的实际例子:

def line_conf(a, b):
   def line(x):
       return a*x + b
   return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))


(6, 25)

Process finished with exit code 0

这个例子中,函数line与环境变量a,b构 成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。

5 os和sys模块的作用?

os与sys模块的官方解释如下:

os: This module provides a portable way of using operating system dependent functionality.

这个模块提供了一种方便的使用操作系统函数的方法。

sys: This module provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter.

这个模块可供访问由解释器使用或维护的变量和与解释器进行交互的函数。

总结就是,os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。

os 常用方法

os.remove(‘path/filename’) 删除文件
os.rename(oldname, newname) 重命名文件
os.walk() 生成目录树下的所有文件名
os.chdir('dirname') 改变目录
os.mkdir/makedirs('dirname')创建目录/多层目录
os.rmdir/removedirs('dirname') 删除目录/多层目录
os.listdir('dirname') 列出指定目录的文件
os.getcwd() 取得当前工作目录
os.chmod() 改变目录权限
os.path.basename(‘path/filename’) 去掉目录路径,返回文件名
os.path.dirname(‘path/filename’) 去掉文件名,返回目录路径
os.path.join(path1[,path2[,...]]) 将分离的各部分组合成一个路径名
os.path.split('path') 返回( dirname(), basename())元组
os.path.splitext() 返回 (filename, extension) 元组
os.path.getatime\ctime\mtime 分别返回最近访问、创建、修改时间
os.path.getsize() 返回文件大小
os.path.exists() 是否存在
os.path.isabs() 是否为绝对路径
os.path.isdir() 是否为目录
os.path.isfile() 是否为文件

sys 常用方法

sys.argv 命令行参数List,第一个元素是程序本身路径
sys.modules.keys() 返回所有已经导入的模块列表
sys.exc_info() 获取当前正在处理的异常类,exc_type、exc_value、exc_traceback当前处理的异常详细信息
sys.exit(n) 退出程序,正常退出时exit(0)
sys.hexversion 获取Python解释程序的版本值,16进制格式如:0x020403F0
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.maxunicode 最大的Unicode值
sys.modules 返回系统导入的模块字段,key是模块名,value是模块
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdout 标准输出
sys.stdin 标准输入
sys.stderr 错误输出
sys.exc_clear() 用来清除当前线程所出现的当前的或最近的错误信息
sys.exec_prefix 返回平台独立的python文件安装的位置
sys.byteorder 本地字节规则的指示器,big-endian平台的值是'big',little-endian平台的值是'little'
sys.copyright 记录python版权相关的东西
sys.api_version 解释器的C的API版本

识别图中二维码,领取python全套视频资料

猜你喜欢

转载自blog.csdn.net/ZhangQiye1993/article/details/81279988