Python Web工程师面试总结(二)- Python基础
上篇笼统地聊了整个知识框架,这一篇会重点谈谈其中的常问的 Python 基础知识部分。
题目:
- 谈谈 Python2 与 Python3 的差别
- Python 中的单下划线与双下划线分别指什么
- 聊聊 Python 中的可变对象与不可变对象
- Python 中的迭代器与生成器是什么
- Python 中的装饰器是什么? 有什么用
- Lambda 表达式是什么?
- 简述库包模块之间的关系
- 如何管理 Python 环境, 为什么要管理?
- Python 中的 with 关键字是什么?
- *args 与 **kwargs 参数分别是什么含义?
- is 与 == 的区别
- init 与 new 的区别
答案:
1. 谈谈 Python2 与 Python3 的差别
- print 语句更换为 print() 函数
- 所有字符串默认均为 Unicode, 不再区分 ASCII 与 Unicode 文本
- 除法运算中两个整数相除可以直接得到浮点数, // 为整除符号不变
Python 2.7.15 (default, May 1 2018, 05:55:50)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1/2
0
>>> 1 // 2
0
>>> 1.0 / 2
0.5
Python 3.6.5 (default, May 11 2018, 13:30:17)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 / 2
0.5
>>> 1 // 2
0
>>> 1.0 // 2
0.0
- 合并 raw_input() 与 input() 为 input(), raw_input() 删除, 可以直接输入数字和文本, 不再区分
- 合并 xrange() 特性到 range(), 移除 xrange()
- dict 的 .keys() .values()函数均返回迭代器而非列表, range, zip 也将返回迭代器, 同样的还有 map(), filter()
- 不等运算符 <> 被移除
- 八进制表达式示例由 0777 变为 0o777
2. Python 中的单下划线与双下划线分别指什么
-
__name__
:一种约定,Python内部的名字,用来与用户自定义的名字区分开,防止冲突 -
_name
:一种约定,用来指定变量私有 -
__name
:解释器用_classname__name来代替这个名字用以区别和其他类相同的命名 -
_
用作一次性或丢弃( throw-away)的名称。按照惯例,这样做可以让阅读你代码的人知道,这是个不会被使用的特定名称。举个例子,你可能无所谓一个循环计数的值:
n = 42
for _ in range(n):
do_something()
3. 聊聊 Python 中的可变对象与不可变对象
- 不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。(也就是说改变了就等于申请了一块新的空间)
- 可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。
最简单的例子就是看变量的 id
Python 3.6.5 (default, May 11 2018, 13:30:17)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> # string is not constant
>>> a = 'hhh'
>>> id1 = id(a)
>>> a += 'lll'
>>> a
'hhhlll'
>>> id2 = id(a)
>>> id1 == id2
False
>>> # list is inconstant
>>> b = [1,2,3]
>>> id3 = id(b)
>>> b.append(4)
>>> id4 = id(b)
>>> id3 == id4
4. Python 中的迭代器与生成器是什么
**注意是生成器不是生成式. **
生成器: 如果列表元素可以按照某种算法推算出来,那么就可以在后续循环中不断推算得出元素, 不必创建完整的list,从而节省大量的内存. 在Python中,这种一边循环一边计算的机制,称为生成器:generator. 生成器是迭代器的一种. 生成器一次只能产生一个值, 而不是整个列表返回.
创建一个generator,有很多种方法,第一种方法很简单,只有把一个列表生成式的 [] 中括号改为 () 小括号,就创建一个generator
>>> li = [x for x in range(5)]
>>> li
[0, 1, 2, 3, 4]
>>> gen = (x for x in range(5))
>>> gen
<generator object <genexpr> at 0x7f42b26b3a98>
>>> gen.__next__()
0
>>> gen.__next__()
1
>>> next(gen)
2
>>> next(gen)
3
>>> next(gen)
4
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> gen = (x for x in range(5))
>>> for i in gen:
... print(i)
...
0
1
2
3
4
迭代器:
迭代器与可迭代对象是被包含的关系, 可迭代对象不一定是迭代器.
- 凡是可作用于 for 循环的对象都是 Iterable 类型;
- 凡是可作用于 next()函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列;(惰性计算的一个例子是
if a and b
当 a 为 False, 则 b 不再计算, 结果肯定是 False, 用在迭代器则是如果你不访问该值, 则不取出该值 ) - 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
5. Python 中的装饰器是什么? 有什么用
装饰器本质上是一个Python函数,它可以让其它函数在不作任何变动的情况下增加额外功能(也就是说, 是可以继承的),装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等。有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。
6. Lambda 表达式是什么?
传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
例如lambda x: x * x
实际上就是:
def f(x):
return x * x
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数, 同时匿名函数既然是一个对象, 也可以作为返回值:
>>> f = lambda x: x*x
>>> f(5)
25
>>> def test(x, y):
... return lambda: x*x + y
...
>>> test(5, 1)
<function test.<locals>.<lambda> at 0x7f42b26a5ae8>
7. 简述库包模块之间的关系
Python 中的库是指具有相关功能模块的集合。一个库通常由多个模块. 如 sys
是一个和系统操作相关的标准库.
包是模块文件的打包, 包(Package)下面会有一个 __init__.py
的文件. 若有子目录且子目录下也有 __init__.py
的文件, 则称子目录为子包.
模块是一个个.py文件, 里面包含函数和变量等.
8. 如何管理 Python 环境, 为什么要管理?
使用 Virtualenv, anaconda 等包管理工具进行管理. 管理的原因是为了在系统上开发多个项目时不会产生库和包的污染, 也可以让 Python 的不同版本共存. 不同项目使用不同的虚拟环境, 在项目最后导出所需的包的时候也不会产生包的并集, 所需多少就是多少.
9.Python 中的 with 关键字是什么?
with 是与异常处理相关的功能, with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
格式如下:
with context_expression [as target(s)]:
with-body
访问语句中的 with-body
前会调用 __enter__()
结束时调用 __exit__()
, 无论是否有异常, __exit__()
都会做好收尾工具, 不需要再try catch
10.
*args
与**kwargs
参数分别是什么含义?
如果我们不确定往一个函数中传入多少参数,或者我们希望以元组(tuple)或者列表(list)的形式传参数的时候,我们可以使用*args
(单星号)。如果我们不知道往函数中传递多少个关键词参数或者想传入字典的值作为关键词参数的时候我们可以使用**kwargs
(双星号)
11. is 与 == 的区别
前面提到用 id 是否相等来判断变量是否可变, 我们用了 ==
符号, 如果直接用 is
关键词, 那么不用获得 id 就可以判断两个是否是同一个变量.
下面给出一个例子, a 先赋值给 c, c 成为 a 的引用, 分别拼接相同的字符串, 此时二者仍相等, 但是此时 a is not c
>>> a = 'hhh'
>>> c = a
>>> a += 'lll'
>>> c += 'lll'
>>> a == c
True
>>> a is c
False
从这里可以知道 is
判断的是是否指向同一个内存空间, ==
判断的是纯粹的值是否相等.
__init__
与__new__
的区别:
-
__init__
方法为初始化方法,__new__
方法才是真正的构造函数。 -
__new__
方法默认返回实例对象供__init__
方法、实例方法使用。 -
__init__
方法为初始化方法,为类的实例提供一些属性或完成一些动作。 -
__new__
方法创建实例对象供__init__
方法使用,__init__
方法定制实例对象。(因此,__new__
在__init__
之前被调用) -
__new__
是一个静态方法,而__init__
是一个实例方法。
Reference:
What’s New In Python 3.0
Underscores in Python
python 生成器和迭代器有这篇就够了
The ‘with’ statement
python中模块,包,库
廖雪峰的网站
公众号: 程序员的碎碎念