前言
之所以在这里写下 python 爬虫常见面试题及解答, 一是用作笔记, 方便日后回忆; 二是给自己一个和大家交流的机会, 互相学习, 进步, 希望不正之处大家能给予指正; 三是我也是互联网寒潮下岗的那批人之一, 为了找工作而做准备.
一, 题目部分
1,python 中常用的数据结构有哪些? 请简要介绍一下.
2, 简要描述 python 中单引号, 双引号, 三引号的区别.
3, 如何在一个 function 里设置一个全局的变量.
4,python 里面如何拷贝一个对象?(赋值, 浅拷贝, 深拷贝的区别)
5, 如果 custname 字符串的编码格式为 uft-8, 如何将 custname 的内容转化为 gb18030 的字符串?
6, 请写出一段 python 代码实现删除 list 中的重复元素.
7, 这两个参数是什么意思? args 和 kwargs.
8,
(1)统计如下 list 单词及其出现的次数.
a=['apple', 'banana', 'apple', 'tomato', 'orange', 'apple', 'banana', 'watermeton']
(2)给列表中的字典排序: 例如有如下 list 对象:
alist=[{"name":"a", "age":20}, {"name":"b", "age":30}, {"name":"c", "age":25}] 将 alist 中的元素按照 age 从小到大排序.
(3)写出下列代码的运行结果
- a = 1
- def fun(a):
- a = 2
- fun(a)
- print(a)
- a = []
- def fun(a):
- a.append(1)
- fun(a)
- print(a)
- class Person:
- name = 'Lily'
- p1 = Person()
- p2 = Person()
- p1.name = 'Bob'
- print(p1.name)
- print(p2.name)
- print(Person.name)
二, 解答部分
注: 以下答案, 均为 google 后结合自己学所知识回答, 可能会有不正确的地方, 错误之处希望大家帮我指正出来, 谢谢.
1,python 中常用的数据结构有哪些? 请简要介绍一下.
python 中常见的数据结构有: 列表 (list), 字典(dict), 元组(tuple), 字符串(string), 集合(set), 数字(int 或 long 或 float...) 等.
其中, 列表, 元祖和字符串可以统一归为序列类, 即这三种数据结构中的元素是有序的. 比如, 他们都有索引 (下标) 操作, 还有切片, 相加和长度 (len), 最大值(max), 最小值(min) 操作. 这是他们的共同点.
补充: python 中常见的数据结构可以统称为容器 (container). 序列(如列表和元组), 映射(如字典) 以及集合 (set) 是三类主要的容器.
另外, 关于这个问题, 面试官很容易引出另一个问题: python 中的哪些数据类型是可变的, 哪些是不可变的?
首先, 可变 / 不可变是针对该对象所指向的内存中的值是否可变来判断的. 如可变类型的数据类型有列表和字典. 不可变类型的数据类型有字符串, 元组, 集合, 数字.
就举个最简单的数字的例子, python 中有小整数池的概念, 即 [-5,256] 范围内的整数, python 解释器对他们做了特殊处理, 都放在内存中的固定位置, 不会因为你的操作二发生变化.
现在: a = 1 , 然后我们又重新对 a 赋值, a = 2, 在重新赋值的过程中, 整数 1 所对应的内存地址没有和数字的大小都没有发生变化, 还在内存中的固定位置. 整数 2 也是如此. 变化的是 a 的指针 (这里引用 C 中的概念) 从指向数字 1 变成数字 2.a 对象指向的内存中的值没有发生变化, 因此数字是不可变类型的数据类型. 字符串, 集合也是同理.
2, 简要描述 python 中单引号, 双引号, 三引号的区别.
首先, 单引号和双引号在使用时基本上没有什么区别, 唯一需要注意的是: 当字符串中有单引号时, 最好在外面使用双引号; 当有双引号时, 最好在外面使用单引号.
三引号一般不常用, 除了用来做注释之外, 还可以用来打印多行字符串. 特殊用途, 是可以打印多行字符串.
print('''i
love
you''') #特殊功能, 可以直接打印多行内容, 而前面两种情况需要显示输入 \ n 才能换行
输出结果:
1 i 2 love 3 you
而单引号和双引号如果想要实现上面的效果, 需要加上换行符.
1 print('i\nlove\nyou')
3, 如何在一个 function 里设置一个全局的变量.
先说概念, 全局变量是指定义在函数外部的变量. 全局变量的作用域为全局.
局部变量是指定义在函数内部的变量. 局部变量的作用域为函数内, 除了函数就无效了.
这里举个例子, 如果把函数比作国家, 那么全局就是全球, 全局变量好比是阿拉伯数字, 每个国家都认识.
所以, 根据定义可以知道, 在函数内部是无法定义一个全局变量的, 只能做到修改已经定义的全局变量.
4,python 里面如何拷贝一个对象?(赋值, 浅拷贝, 深拷贝的区别)
在 python 中如何拷贝一个对象是需要根据具体的需求来定的.
(1)赋值: 其实就是对象的引用. 相当于 C 的指针, 修改了其中一个对象, 另一个跟着改变. 注意对于不可变对象而言, 如果修改了其中一个对象, 就相当于修改它的指针指向, 另一个对象是不会跟着变化的.
- a = ['1', '2'] # a 是一个可变对象
- b = a
- a = a.pop()
- print(b) # 修改了 a,b 也跟着变
输出结果:
1 ['1']
当 a 为不可变对象时:
- a = 1
- b = a
- a = 2
- print('b = {}'.format(b))
输出结果:
1 b = 1
(2)浅拷贝: 拷贝父对象, 但是不会拷贝父对象的子对象.(具体的方法有: b = copy.copy(a), 切片如 b = a[1:4])
- a = {1: [1, 2, 3]}
- b = a.copy()
- print(a, b)
- a[1].append(4)
- print(a, b)
输出结果为:
- {
- 1: [1, 2, 3]
- } {
- 1: [1, 2, 3]
- }
- {
- 1: [1, 2, 3, 4]
- } {
- 1: [1, 2, 3, 4]
- }
当 a 为不可变对象时:
- import copy
- a = 'TEST_STRING'
- b = copy.copy(a)
- print(a, b)
- a = a.lower()
- print(a, b)
输出结果:
- TEST_STRING TEST_STRING
- test_string TEST_STRING
(3)深拷贝: 完全拷贝了父对象和子对象(具体的方法有: b = copy.deepcopy(a))
- import copy
- a = {1: [1, 2, 3]}
- b = copy.deepcopy(a)
- print(a, b)
- a[1].append(4)
- print(a, b)
输出结果:
- {1: [1, 2, 3]} {1: [1, 2, 3]}
- {1: [1, 2, 3, 4]} {1: [1, 2, 3]}
当 a 为不可变对象时:
- import copy
- a = 'TEST_STRING'
- b = copy.deepcopy(a)
- print(a, b)
- a = a.lower()
- print(a, b)
输出结果:
- TEST_STRING TEST_STRING
- test_string TEST_STRING
下面是图解:
1,b = a: 赋值引用, a 和 b 都指向同一个对象.
2,b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象, 但他们的子对象还是指向统一对象(是引用).
3,b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象, 两者是完全独立的.
总结:
(1)当对象为不可变类型时, 不论是赋值, 浅拷贝还是深拷贝, 那么改变其中一个值时, 另一个都是不会跟着变化的.
(2)当对象为可变对象时, 如果是赋值和浅拷贝, 那么改变其中任意一个值, 那么另一个会跟着发生变化的; 如果是深拷贝, 是不会跟着发生改变的.
啊, 这一题答案真的是好长啊, 累到掉渣! 歇会儿...
5, 如果 custname 字符串的编码格式为 uft-8, 如何将 custname 的内容转化为 gb18030 的字符串?
先将 custname 编码格式转换为 unicode, 在转换为 gb18030. 即 custname.decode('utf-8').encode('gb18030').
注意: unicode 编码是一种二进制编码, 是转换编码的中间桥梁. 比如需要将 utf-8 转换为 gbk, 那么就需要先转换为 unicode(decode), 再转为 gbk(encode).
6, 请写出一段 python 代码实现删除 list 中的重复元素.
两种方法:
(1)利用字典的 fromkeys 来自动过滤重复值
(2)利用集合 set 的特性, 元素是非重复的
方法一:
- a = [1, 2, 3, 4, 5, 2, 3]
- def fun1(a):
- a = list(set(a))
- print(a)
- fun1(a)
方法二:
- a = [1, 2, 3, 4, 5, 2, 3]
- def fun1(a):
- b = {}
- b = b.fromkeys(a)
- c = list(b.keys())
- print(c)
- c = fun1(a)
7, 这两个参数是什么意思? args 和 kwargs.
首先, 我想说的是 * args 和 **kwargs 并不是必须这样写, 只有前面的 * 和 ** 才是必须的. 你可以写成 * var 和 **vars. 而写成 * args 和 **kwargs 只是约定俗称的一个命名规定.
*args 和 **kwargs 主要用于函数定义, 你可以将不定量的参数传递给一个函数. 其中,*args 是用来发送一个非键值对的可变数量的参数列表给一个函数;**kwargs 允许你将不定长度的键值对, 作为参数传递给一个函数. 如果你想要在一个函数里处理带名字的参数, 你应该使用 **kwargs.
- def import_args(test, *args):
- print('param1', test)
- for item in args:
- print('other param', item)
- import_args('123', 'hello', '2019')
这里传递了 3 个参数, 按位置传参,'123'为 test 传参,'hello'和'2019'为 * args 传参, 这里传了 2 个参数.
注意, 看下面的 * args 的另一种用法: 用来解压数据.
- def import_args(test, *args):
- print('param1', test)
- for item in args:
- print('other param', item)
- args = ['hello', '2019']
- import_args('123', *args)
输出结果:
- param1 123
- other param hello
- other param 2019
这段代码和上面的效果是一样的, 但是这里第 8 行的 * args 和第 1 行的 * args 可是不一样的. 第一行是表示函数可以接受不定数量的非键值对的参数, 用来传参使用的. 第八行是用来解压列表
['hello', '2019']的每一项数据的, 用来解压参数的. 这是 * args 的两种用法, 也可说是 * 的两种用法, 因为 args 是可变的.
接下来说说 **kwargs.
- def import_kwargs(test, **kwargs):
- print('param1', test)
- for key, value in kwargs.items():
- print(key, value)
- d = {'name': 'jack', 'age': 26}
- import_kwargs('123', **d)
**kwargs 用来传递带键值对的参数, 而 ** 也是用来解压字典容器内的参数.
输出结果:
- param1 123
- name jack
- age 26
总结:*args 和 **kwargs 都是用于函数中传递参数的,*args 传递的是非键值对的参数,**kwargs 传递的是带键值对的参数, 如果还有普通参数需要传递, 那么应该先传递普通的参数.
8,
(1)统计如下 list 单词及其出现的次数.
a=['apple', 'banana', 'apple', 'tomato', 'orange', 'apple', 'banana', 'watermeton']
方法一:
利用字典.
- a = ['apple', 'banana', 'apple', 'tomato', 'orange', 'apple', 'banana', 'watermeton']
- dic = {}
- for key in a:
- dic[key] = dic.get(key, 0) + 1
- print(dic)
输出结果:
1 {'apple': 3, 'banana': 2, 'tomato': 1, 'orange': 1, 'watermeton': 1}
方法二:
利用 python 的 collections 包.
- from collections import Counter
- a = ['apple', 'banana', 'apple', 'tomato', 'orange', 'apple', 'banana', 'watermeton']
- d = Counter(a)
- print(d)
输出结果:
1 Counter({'apple': 3, 'banana': 2, 'tomato': 1, 'orange': 1, 'watermeton': 1}) # 是一个类似字典的结构
(2)给列表中的字典排序: 例如有如下 list 对象:
alist=[{"name":"a", "age":20}, {"name":"b", "age":30}, {"name":"c", "age":25}] 将 alist 中的元素按照 age 从小到大排序.
利用 list 的内建函数, list.sort()来进行排序.
- alist = [{"name": "a", "age": 20}, {"name": "b", "age": 30}, {"name": "c", "age": 25}]
- alist.sort(key=lambda x: x['age'])
- print(alist)
这是一种效率很高的排序方法.
输出结果:
1 [{'name': 'a', 'age': 20}, {'name': 'c', 'age': 25}, {'name': 'b', 'age': 30}]
(3)写出下列代码的运行结果
第一段代码的运行结果为: 1
分析, 在函数外面定义了一个全局变量 a 为 1, 在函数内部定义了一个局部变量 a 为 2. 局部变量在离开函数后就失效了.
所以, 结果为全局变量的 a 的值. 如果在 a=2 之前加上 global a, 声明为全局变量, 那么结果为 2.
第二段代码的运行结果为:[1]
这是因为, 将 a 传入到 function 中, 这相当于对 a 进行赋值引用. 由于 a 是可变类型的, 所以在函数内部修改 a 的时候, 外部的全局变量 a 也跟着变化.
第三段代码的运行结果为:
1 Bob 2 Lily 3 Lily
以上.
来源: https://www.cnblogs.com/tianyiliang/p/10212951.html