@ 谈谈对 Python 和其他语言的区别
-
Python 是一门语法简洁优美,功能强大无比,应用领域非常广泛,具有强大完备的第三方库,他是一门强类型的可移植、可扩展,可嵌入的解释型编程语言,属于动态语言。
-
拿 C 语言和 Python 比: Python 的第三方类库比较齐全并且使用简洁,很少代码就能实现一些功能,如果用 C 去实现相同的功能可能就比较复杂。但是对于速度来说 Python 的运行速度相较于 C 就比较慢了。所以有利的同时也有弊端,毕竟我们的学习成本降低了。
@ 简述解释型和编译型编程语言
-
解释型语言是在运行程序的时候才翻译,每执行一次,要翻译一次,效率较低。 编译型就是直接编译成机型可以执行的,只翻译一次,所以效率相对来说较高。
@ Python 的解释器种类以及相关特点?
-
CPython c 语言开发的,使用最广的解释器
-
IPython 基于 cPython 之上的一个交互式计时器,交互方式增强功能和 cPython 一样
-
PyPy 目标是执行效率,采用 JIT 技术。对 Python 代码进行动态编译,提高执行效率
-
JPython 运行在 Java 上的解释器,直接把 Python 代码编译成 Java 字节码执行
-
IronPython 运行在微软 .NET 平台上的解释器,把 Python 编译成 . NET 的字节码。
@ Python3 和 Python2 的区别?
-
这里例举 5 条
1、 print 在 Python3 中是函数必须加括号,Python2 中 print 为 class。
2、 Python2 中使用 xrange,Python3 使用 range。
3、 Python2 中默认的字符串类型默认是 ASCII,
Python3 中默认的字符串类型是Unicode。
4、 Python2 中 / 的结果是整型,Python3 中是浮点类型。
5、 Python2 中声明元类:_metaclass_ = MetaClass,
Python3 中声明元类:class newclass(metaclass=MetaClass):pass。
@ Python3 和 Python2 中 int 和 long 区别?
-
Python2 有 int 和 long 类型。int 类型最大值不能超过 sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比 int 类型表示的数字范围更大。
-
在 Python3 里,只有一种整数类型 int,大多数情况下,和 Python2中的长整型类似。
@ xrange 和 range 的区别?
-
xrange 是在 Python2 中的用法,Python3 中只有 range xrange 用法与 range 完全相同,所不同的是生成的不是一个 list 对象,而是一个生成器。
@ 编码规范
-
什么是 PEP8?
-
PEP8 通常会听别人提到,但是具体的指什么内容呢,简单介绍下。 《Python Enhancement Proposal #8》(8 号 Python 增强提案)又叫 PEP8,他针对的 Python 代码格式而编订的风格指南。
@ 了解 Python 之禅么?
-
通过 import this 语句可以获取其具体的内容。它告诉大家如何写出高效整洁的代码。
@ 了解 DocStrings 么?
-
DocStrings 文档字符串是一个重要工具,用于解释文档程序,帮助你的程序文档更加简单易懂。主要是解释代码作用的。
-
函数的 __doc__ 变量内容就是这个DocStrings
def doc_string_test():
''' 本函数用来测试 DocStrings
DocStrings 文档字符串用于解释文档程序,
主要是解释代码作用的。
类和模块都能用。
'''
pass
print(doc_string_test.__doc__)
@ 了解类型注解么?
答:PEP 484 引入了类型提示,这使得可以对 Python 代码进行静态类型检查。 在使用 Ide 的时候可以获取到参数的类型,更方便传入参数。使用格式如下
def foo(num: int) -> None:
print(f"接收到的数字是:{num}")
介绍下这个简单例子,我们可以在函数的参数部分使用参数名+:+类型,来指定参数可以接受的类型,这里的话就是 num 参数为 int 类型,然后后面->接的是返回值的类型。这里返回值为 None,然后通过 fstring 格式化字符串输出传入的数字。
@ 例举你知道 Python 对象的命名规范,例如方法或者类等
-
类:总是使用首字母大写单词串,如 MyClass。内部类可以使用额外的前导下划线。
-
变量:小写,由下划线连接各个单词。方法名类似
-
常量:常量名所有字母大写 等
@ Python 中的注释有几种?
-
总体来说分为两种,单行注释和多行注释。
-
单行注释在行首是 #
-
多行注释可以使用三个单引号或三个双引号,包括要注释的内容。
@ 如何优雅的给一个函数加注释?
-
可以使用 docstring 配合类型注解
@ Python 代码缩进中是否支持 Tab 键 和 空格混用。
-
不允许 tab 键和空格键混用,这种现象在使用 sublime 的时候尤为明显。
-
一般推荐使用 4 个空格替代 tab 键。
@ 是否可以在一句 import 中导入多个库?
-
可以是可以,但是不推荐。因为一次导入多个模块可读性不是很好
-
一行导入一个模块会比较好。
-
同样的尽量少用 from modulename import *,因为判断某个函数或者属性的来源有些困难,不方便调试,可读性也降低了。
@ 在给 Py 文件命名的时候需要注意什么?
-
给文件命名的时候不要和标准库中的一些模块重复,比如 abc。
-
另外要名字要有意义,不建议数字开头或者中文命名。
@ 例举几个规范 Python 代码风格的工具
-
pylint
-
flake8
数据类型-字符串
@ 列举 Python 中的基本数据类型?
-
Python3 中有六个标准的数据类型
-
字符串(String)
-
数字(Digit)
-
列表(List)
-
元组(Tuple)
-
集合(Sets)
-
字典(Dictionary)
@ 如何区别可变数据类型和不可变数据类型
-
从对象内存地址方向来说
-
可变数据类型:在内存地址不变的情况下,值可改变(列表和字典是可变类型,但是字典中的 key 值必须是不可变类型)
-
不可变数据类型:内存改变,值也跟着改变。(数字,字符串,布尔类型,都是不可变类型)可以通过 id() 方法进行内存地址的检测。
@ 将"hello world"转换为首字母大写"Hello World"
-
这个得看清题目是要求两个单词首字母都要大写
print("hello world".title())
-
title() 方法返回"标题化"的字符串,就是说所有单词都是以大写开始,其余字母均为小写(见 istitle())
-
补充: istitle() 方法检测字符串中所有的单词拼写首字母是否为大写,且其他字母为小写。
-
如果字符串中所有的单词拼写首字母是否为大写,且其他字母为小写则返回 True,否则返回 False.
@ 如何检测字符串中只含有数字?
-
可以通过 isdigit 方法,例子如下
s1 = "12223".isdigit()
print(s1)
s2 = "12223a".isdigit()
print(s2)
#结果如下:
#True
#False
@ 将字符串"hello world"进行反转
s1 = "hello world"[::-1]
print(s1)
@ Python 中的字符串格式化方式你知道哪些?
-
%s,
-
format,
-
fstring(Python3.6 开始才支持,现在推荐的写法)
@ 有一个字符串开头和末尾都有空格,比如“ adabdw ”,要求写一个函数把这个字符串的前后空格都去掉。
-
因为题目要是写一个函数所以我们不能直接使用 strip,不过我们可以把它封装到函数啊
def strip_function(s1):
return s1.strip()
s1 = " adabdw "
print(strip_function(s1))
@ 获取字符串”123456“最后的两个字符。
-
切片使用的考察,最后两个即开始索引是 -2,代码如下
a = "123456"
print(a[-2::])
@ 一个编码为 GBK 的字符串 S,要将其转成 UTF-8 编码的字符串,应如何操作?
a= "S".encode("gbk").decode("utf-8",'ignore')
print(a)
@ 怎样将字符串转换为小写
-
使用字符串的 lower() 方法。
@ 单引号、双引号、三引号的区别?
-
单独使用单引号和双引号没什么区别,但是如果引号里面还需要使用引号的时候,就需要这两个配合使用了,
-
然后说三引号,同样的三引号也分为三单引号和三双引号,两个都可以声名长的字符串时候使用,如果使用
docstring 就需要使用三双引号。
数据类型 - 列表
-
已知 AList = [1,2,3,1,2],对 AList 列表元素去重,写出具体过程。
list(set(AList))
@ 如何实现 "1,2,3" 变成 ["1","2","3"]
s = "1,2,3"
print(s.split(","))
@ 给定两个 list,A 和 B,找出相同元素和不同元素
-
A、B 中相同元素:print(set(A)&set(B))
-
A、B 中不同元素:print(set(A)^set(B))
@ [[1,2],[3,4],[5,6]] 一行代码展开该列表,得出 [1,2,3,4,5,6]
l = [[1,2],[3,4],[5,6]]
x=[j for i in l for j in i]
print(x)
@ 合并列表 [1,5,7,9] 和 [2,2,6,8]
# 使用 extend 和 + 都可以。
a = [1,5,7,9]
b = [2,2,6,8]
a.extend(b)
print(a)
@ 如何打乱一个列表的元素?
import random
a = [1, 2, 3, 4, 5]
random.shuffle(a)
print(a)
数据类型 - 字典
@ 字典操作中 del 和 pop 有什么区别
-
del 可以根据索引(元素所在位置)来删除的,没有返回值。
-
pop 可以根据索引弹出一个值,然后可以接收它的返回值。
@ 按照字典的内的年龄排序
d1 = [
{'name': 'alice', 'age': 38},
{'name': 'bob', 'age': 18},
{'name': 'Carl', 'age': 28},
]
print(sorted(d1, key=lambda x: x["age"]))
@ 请合并下面两个字典 a = {"A":1,"B":2}, b = {"C":3,"D": 4}
-
合并字典方法很多,可以使用 a.update(b) 或者下面字典解包的方式
a = {"A":1,"B":2}
b = {"C":3,"D":4}
print({**a,**b})
@ 如何使用生成式的方式生成一个字典,写一段功能代码。
# 需求 3: 把字典的 key 和 value 值调换;
d = {'a':'1', 'b':'2'}
print({v: k for k, v in d.items()})
@ 如何把元组 ("a","b") 和元组 (1,2),变为字典 {"a":1,"b":2}
-
zip 的使用,但是最后记得把 zip 对象再转换为字典。
a = ('a', 'b')
b = (1, 2)
print(dict(zip(a, b)))
数据类型 - 综合
-
下列字典对象键类型不正确的是?
A:{1: 0,2: 0,3: 0}
B:{"a": 0, "b": 0, "c": 0}
C:{(1,2): 0, (2,3): 0}
D:{[1,2]:0, [2,3]:0}
D 因为只有可 hash 的对象才能做字典的键,列表是可变类型不是可 hash 对象,所以不能用列表做为字典的键。
@ 如何交换字典 {"A":1,"B":2}的键和值
s = {"A": 1,"B": 2}
#方法一:
dict_new = {value:key for key, value in s.items()}
# 方法二:
new_s = dict(zip(s.values(), s.keys()))
@ Python 里面如何实现 tuple 和 list 的转换?
-
Python 中的类型转换,一般通过类型强转即可完成 tuple 转 list 是 list() 方法 list 转 tuple 使用 tuple() 方法
@ 我们知道对于列表可以使用切片操作进行部分元素的选择,那么如何对生成器类型的对象实现相同的功能呢?
答: 这个题目考察了 Python 标准库的 itertools 模快的掌握情况,该模块提供了操作生成器的一些方法。 对于生成器类型我们使用 islice 方法来实现切片的功能。例子如下
from itertools import islice
gen = iter(range(10)) #iter()函数用来生成迭代器
#第一个参数是迭代器,第二个参数起始索引,第三个参数结束索引,不支持负数索引
for i in islice(gen,0,4):
print(i)
@ 请将 [i for i in range(3)] 改成生成器
-
通过把列表生产式的中括号,改为小括号我们就实现了生成器的功能即,
(i for i in range(3))
@ a="hello" 和 b="你好" 编码成 bytes 类型
-
这个题目一共三种方式,
-
在字符串的前面加一个 b
-
可以使用 bytes 方法
-
第三种使用字符串 encode 方法
-
具体代码如下,abc 代表三种情况
a = b"hello"
b = bytes("你好", "utf-8")
c = "你好".encode("utf-8")
print(a, b, c)
@ {1, 2, 3} 的数据类型
-
set型,无序不重复集合
-
print({5,5,5}) 输出 {5}
@ 如果创建空的set类型集合,和空字典怎么区分呢
-
规定定义空无序集合,只能用a =set()
@ 下面的代码输出结果是什么?]
a = (1,2,3,[4,5,6,7],8)
a[2] = 2
-
我们知道元组里的元素是不能改变的所以这个题目的答案是出现异常。
@ 下面的代码输出的结果是什么?
a = (1,2,3,[4,5,6,7],8)
a[3][0] = 2
-
前面我说了元组的里元素是不能改变的,这句话严格来说是不准确的,如果元组里面元素本身就是可变类型,比如列表,那么在操作这个元素里的对象时,其内存地址也是不变的。a[3] 对应的元素是列表,然后对列表第一个元素赋值,所以最后的结果是: (1,2,3,[2,5,6,7],8)
操作类题目
-
Python 交换两个变量的值
-
在 Python 中交换两个对象的值通过下面的方式即可
a , b = b ,a
-
但是需要强调的是这并不是元组解包,
-
通过 dis 模块可以发现,这是交换操作的字节码是 ROT_TWO,意思是在栈的顶端做两个值的互换操作。
@ 字符转换
a = ord('A') # 字串转数值
c = chr(96) # 数值转字符
@ 在读文件操作的时候会使用 read、readline 或者 readlines,简述它们各自的作用
-
read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。
-
如果希望一行一行的输出那么就可以使用 readline(),该方法会把文件的内容加载到内存,所以对于对于大文件的读取操作来说非常的消耗内存资源,
-
此时就可以通过 readlines 方法,将文件的句柄生成一个生产器,然后去读就可以了。
@ json 序列化时,可以处理的数据类型有哪些?如何定制支持 datetime 类型?
-
可以处理的数据类型是 str、int、list、tuple、dict、bool、None, 因为 datetime 类不支持 json 序列化,所以我们对它进行拓展。
# 自定义时间序列化
import json
from datetime import datetime, date
# JSONEncoder 不知道怎么去把这个数据转换成 json 字符串的时候
# ,它就会去调 default()函数,所以都是重写这个函数来处理它本身不支持的数据类型,
# default()函数默#认是直接抛异常的。
class DateToJson(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, obj)
d = {'name': 'cxa', 'data': datetime.now()}
print(json.dumps(d, cls=DateToJson))
@ json 序列化时,默认遇到中文会转换成 unicode,如果想要保留中文怎么办?
-
可以通过 json.dumps 的 ensure_ascii 参数解决,代码示例如下:
import json
a=json.dumps({"name":"张三"},ensure_ascii=False)
print(a)
@ 有两个磁盘文件 A 和 B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列),输出到一个新文件 C 中。
#文件 A.txt 内容为 ASDCF
#文件 B.txt 内容为 EFGGTG
with open("A.txt") as f1:
f1_txt = f1.readline()
with open("B.txt") as f2:
f2_txt = f2.readline()
f3_txt = f1_txt + f2_txt
f3_list = sorted(f3_txt)
with open("C.txt", "a+") as f:
f.write("".join(f3_list))
-
输出的文件 C 的内容为 ACDEFFGGGST
@ 如果当前的日期为 20190530,要求写一个函数输出 N 天后的日期,(比如 N 为 2,则输出 20190601)。
-
这个题目考察的是 datetime 里的 timedelta 方法的使用,参数可选、默认值都为 0:
datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
通过这个参数可以指定不同的日期类型进行加减操作,这里我们需要改的是 days,代码如下
import datetime
def datetime_operate(n: int):
now = datetime.datetime.now() # 获取当前时间
_new_date = now + datetime.timedelta(days=n) # 获取指定天数后的新日期
new_date = _new_date.strftime("%Y%m%d") # 转换为指定的输出格式
return new_date
if __name__ == '__main__':
print(datetime_operate(4))
@ 写一个函数,接收整数参数 n,返回一个函数,函数的功能是把函数的参数和 n 相乘并把结果返回。
-
这个题目考查了闭包的使用代码示例如下,返回函数之类型是函数对象。
def mul_operate(num): def g(val): return num * val return g m = mul_operate(8) print(m(5))
@ 下面代码会存在什么问题,如何改进?
def strappend(num):
str='first'
for i in range(num):
str+=str(i)
return str
-
首先不应该使用 Python 的内置类似 str 作为变量名这里我把它改为了 s,
-
另外在Python,str 是个不可变对象,每次迭代都会生成新的存储空间
-
num 越大,创建的 str 对象就会越多,内存消耗越大。使用 yield 改成生成器即可,
-
还有一点就是命名规范的位置,函数名改为_分割比较好,完整的代码如下:
def str_append(num): s = 'first' for i in range(num): s += str(i) yield s if __name__ == '__main__': for i in str_append(3): print(i)
@ 一行代码输出 1-100 之间的所有偶数。
-
可以通过列表生成式,然后使用与操作如果如 1 与之后结果为 0 则表明为偶数,等于 1 则为奇数。
# 方法1 print([i for i in range(1, 101) if i & 0x1 == 0]) # 方法2:测试发现方法二效率更高 print(list(range(2, 101, 2))) # 方法3: print([i for i in range(1, 101) if i % 2 == 0])
@ 对 for 的疑问?
-
in 后边的代码是只执行一次还是每次都会执行?
# 实验1:如果能正常输出,就说明range(len)),每次执行,报错则执行一次
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in range(len(a)):
if i % 2 is 0:
del(a[i])
print(a)
# 结果:报错,说明只执行一次,符合预期
-
为了让上代码运行过,于是换个方式实现
# 实验2 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for i, v in enumerate(a): if i % 2 is 0: del(a[i]) print(a) # 结果:[1, 2, 4, 5, 7, 8]
-
思考:实验2竟然运行过了,那不是说明 enumerate(a) 每次都执行?返回才被更新了?
# 实验3:验证enumerate(a)生成的值是否固定 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] del(a[5]) d = enumerate(a) print([k for i, k in d]) # 结果:[0, 1, 2, 3, 4, 6, 7, 8, 9] - 5没了
-
结论:
-
for 循环 in 后边的代码只执行一次
-
enumerate(a) 返回的值不是固定的,会根据a实际情况变,所以使用的时候要注意这点
@ 对 enumerate(a) 的疑问?
-
如果每次调用 enumerate(a) 的值会根据 a 变,那变化的规律是什么?
# 实验4:打印看下单步运行结果 a = [str(i) for i in range(10)] for i, v in enumerate(a): print("执行前-", a) if i % 2 is 0: del(a[i]) print("执行后*", a, i, v, type(v))
-
运行结果
执行前- ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 执行后* ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 0 0 <class 'str'> 执行前- ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 执行后* ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 1 2 <class 'str'> 执行前- ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 执行后* ['1', '2', '4', '5', '6', '7', '8', '9'] 2 3 <class 'str'> 执行前- ['1', '2', '4', '5', '6', '7', '8', '9'] 执行后* ['1', '2', '4', '5', '6', '7', '8', '9'] 3 5 <class 'str'> 执行前- ['1', '2', '4', '5', '6', '7', '8', '9'] 执行后* ['1', '2', '4', '5', '7', '8', '9'] 4 6 <class 'str'> 执行前- ['1', '2', '4', '5', '7', '8', '9'] 执行后* ['1', '2', '4', '5', '7', '8', '9'] 5 8 <class 'str'> 执行前- ['1', '2', '4', '5', '7', '8', '9'] 执行后* ['1', '2', '4', '5', '7', '8'] 6 9 <class 'str'>
-
结论:
-
代码中 enumerate(a) 返回结果是:[(0, a[0]), (1, a[1])......]
-
i 的值固定 +1,不会根据列表内容变
-
每次得迭代值是 i,a[i], 因为 a 得值变了,所以返回得 a[i] 就跟着变
-
猜测迭代结束得条件是:1、列表迭代完成;2、迭代值出错(a[i]不成立)
-
因为a[i] 的 i 超出列表范围时迭代结束,所以函数能正常执行不出错
@ for 迭代的规律是什么?
-
为什么 range(len(a)) 出来的值不会根据 a 的变化变
# 实验5:测试for迭代的规律 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = range(len(a)) print(a == b) for i in a: print(a, i) del(a[i]) print(a)
输出结果:
False
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 0
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9] 2
[1, 2, 4, 5, 6, 7, 8, 9]
[1, 2, 4, 5, 6, 7, 8, 9] 4
[1, 2, 4, 5, 7, 8, 9]
[1, 2, 4, 5, 7, 8, 9] 5
[1, 2, 4, 5, 7, 9]
[1, 2, 4, 5, 7, 9] 7
Traceback (most recent call last):
File "d:/code/test/main.py", line 50, in <module>
del(a[i])
IndexError: list assignment index out of range
-
结论
-
range(len(a)) 返回结果不是 a, 所以实验1中 a 变不会影响到迭代次数
-
for 迭代类似 a[i++],i 的值每次都会加1,所以 a 的值变化会影响迭代返回值
-
for 迭代过程一开始就已经设置好迭代次数,不会因为 a 内容少了就减少
-
a内容减少后迭代超出范围报错,说明 for 的迭代停止只受最开始计算的次数决定。
-
enumerate(a) 出错停止迭代,for 出错直接报错