1. python语言的特性
Python是一门解释型语言, 简单清晰,开源免费,跨平台,有大量第三方库辅助开发,支持面向对象与自动垃圾回收,方便与其他编程语言相互调用。
Python在数据采集、人工智能、WEB后台开发、自动化运维、测试等方向应用广泛。
2. 解释型语言和编译型语言的区别
- 执行方式不同
- 解释型语言:解释型语言的代码在运行时由解释器逐行读取并执行,不需要事先将整个程序编译成机器代码。
- 编译型语言:编译型语言的代码在执行前需要通过编译器编译成机器代码(或者字节码),生成可执行文件。该可执行文件在运行时直接由操作系统加载和执行。
- 开发和调试
- 解释性语言:不需要编译过程,开发者可以快速编写和测试代码,调试过程也比较直观,因为代码的修改可以立即生效。
- 编译型语言:开发过程包括编写代码、编译、链接和执行。调试时可能需要重新编译代码,调试流程相对解释型语言来说稍显复杂。
- 性能
- 解释型语言:解释型语言通常比编译型语言慢,因为每次执行代码时都需要进行解释,而编译型语言的代码在编译阶段已经转换成了机器代码。
- 编译型语言:编译型语言通常运行速度更快,因为编译阶段已经将代码转换为高效的机器代码。不需要在运行时进行解释,因此性能更高。
- 平台依赖性
- 解释性语言:解释型语言的代码在不同平台上运行时,只需要有适配相应平台的解释器即可,无需针对每个平台分别编译。
- 编译型语言:编译型语言的代码通常与具体的硬件和操作系统相关,不同平台可能需要不同的编译过程和生成不同的可执行文件。
- 解释型语言的优点是开发周期短、跨平台能力强,适合快速开发和迭代。
- 编译型语言的优点是执行效率高、优化能力强,适合对性能要求较高的应用。
3. python原生数据类型
- 整型(int):用于表示整数,例如 1, 100, -5 等。
- 浮点型(float):用于表示带有小数点的数字,例如 3.14, -0.001 等。
- 空值(None)
- 布尔型(bool):用于表示逻辑值,True 或 False。
- 字符串型(str):用于表示文本,例如 "hello", 'python' 等。
- 列表(list):用于存储多个元素的有序集合,可以通过索引访问和修改元素。
- 元组(tuple):类似于列表,但是一旦创建就不能被修改(不可变)。
- 集合(set):用于存储独一无二的元素,不支持重复元素,可以进行集合运算。
- 字典(dict):用于存储键值对的数据结构,通过键来快速查找对应的值。
4. python中哪些是可变类型,哪些是不可变类型,有什么区别
可变类型包括:
- 列表(list)
- 集合(set)
- 字典(dict)
不可变类型包括:
- 整型(int)
- 浮点型(float)
- 空值(None)
- 布尔型(bool)
- 字符串型(str)
- 元组(tuple)
区别
-
可变性
- 可变类型的是引用同一个对象,可以对内容进行修改,只是修改了其内容,并不会创建新的对象
- 不可变修改类型一旦创建,其内容就不能更改。如果尝试修改不可变对象,将会创建一个新的对象。
-
存储方式和性能
- 可变类型通常使用更多的内存,因为它们支持动态的插入和删除操作,可能会导致频繁的内存分配和释放。
- 不可变类型由于内容不可改变,可以被安全地共享和重用,这在多线程编程中尤为重要。
-
哈希性
- 可变类型一般是不可哈希的,因为其内容可以改变,如果用作字典的键或集合的元素会导致不一致性
- 不可变类型通常是可哈希的,因为其值不会变化,可以用作字典的键或集合的元素。
5. 常用的数据结构有哪些?各自的特点?
- 列表(List)
- 特点:有序、可变、可包含重复元素。
- 使用场景:适合存储需要频繁修改(增删改查)的数据。
- 元组(Tuple)
- 特点:有序、不可变、可包含重复元素。
- 使用场景:适合存储不需要修改的数据,具有更高的访问速度。
- 集合(Set)
- 特点:无序、不重复、可变。
- 使用场景:适合存储需要唯一性的数据,支持快速的成员检测和集合操作(如并集、交集)。
- 字典(Dictionary)
- 特点:键-值对存储、无序、可变。
- 使用场景:适合存储关联关系的数据,通过键快速访问值。
- 队列(Queue)
- 特点:先进先出(FIFO)、可变。
- 栈(Stack)
- 特点:后进先出(LIFO)、可变。
6. 什么是面向对象
7. 什么是鸭子类型
不关注对象类型本身,只关注是否有相对应的方法,
比如所我看到一个鸟,它走起路来想鸭子,它的叫声想鸭子,它游泳想鸭子,那么无论它本身是什么,我都可以把它当成一个鸭子
class Duck:
def walk(self):
print("鸭子在走路")
def swim(self):
print("鸭子在游泳")
class Bird:
def walk(self):
print("鸭子在走路")
def swim(self):
print("鸭子在游泳")
d = Duck()
b = Bird()
d.walk()
b.walk()
d.swim()
b.swim()
8. 常用的魔法函数有哪些
__new__:构造函数,会在创建实例的时候自动执行此函数,将此函数的返回值传入到初始化函数的self参数中
__init__:初始化函数,当对象实例出来的时候会紧接着执行初始化函数
__str__:转字符串函数,当在其他地方直接输出实例对象的时候会调用此函数,可以自定义输出的格式
__del__:析构函数,当有实例对象销毁的时候会自动执行此函数
__name__:获取但当前方法名或者类名
9. 简单说一下python中的序列化
序列化:将不能直接存储的数据变成可以存储的数据(列表,字典->str)
反序列化:将存储文件的数据拿出来恢复原样(str->列表,字典)
python中可以使用json和pickle进行序列化和反序列化
- json是跨平台的,任何语言都能使用
- 序列化:使用json的dumps方法进行序列化
- json序列化时,默认遇到中文会转为unicode,如果想保留中文就在dumps方法添加一个ensure_ascii关键字参数,将他的值设置为False
- 反序列化:使用json的loads方法进行序列化
- 序列化:使用json的dumps方法进行序列化
- pickel只能在python中使用,进行二进制的序列化,使用方法和json是一样的
- 字符串反序列化还有一种方法是使用eval函数,eval(str)会将str转为字典进行返回
并不是所有的类型都能够被反序列化,比如集合,datetime,和一些类的实例都是不能够被反序列化的,不能被反序列化的数据会被存储到JSONEncoder这个类的default方法的参数o中,我们可以写一个类继承JSONEncoder这个类,重写其中的default方法,根据不能反序列化的类型给他转变为可序列化的类型即可。
如果将序列化后的集合和datetime模块进行反序列化,那么就需要在写一个类继承JOSNDecoder,重写其中的decode方法,根据需要反序列化的键对其进行更改
9.1 json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型
# json能支持int\float\str\bool\None\list\tuple\dict八种基本类型
# 典型的set类型,datetime类型不能被反序列化
import json
from datetime import datetime
from json import JSONEncoder, JSONDecoder
class Person:
def __init__(self, name,age):
self.name = name
self.age = age
obj = {
'k1': 10,
'k2': 3.14,
'k3': [],
'k4': (1, 2), # 元组会被序列化为列表
'k5': None, # 这个会被序列化为null
'k6': True, # 这个会被序列化为true
'k7': 'str',
'k8': {'a', 'b'}, # 集合是不支持序列化的
'k9': datetime.today(), # datetime类型也是不支持序列化的
'k10': Person("张三", 18)
}
# 默认不支持序列化的类型都会传入JSONEncoder中的default中的o形参中
# 我们只需要创建一个类,然后重写这个方法进行序列化之前的拦截即可
# 使用JSONEncode这个类
class MyJSONEncoder(JSONEncoder):
def default(self, o):
# 对不可序列化的类型进行拦截转换类型然后返回
if isinstance(o, datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
if isinstance(o, set):
return list(o)
if isinstance(o, Person):
return {'name': o.name, 'age': o.age}
str_json = json.dumps(obj, cls=MyJSONEncoder, ensure_ascii=False)
print(str_json)
# 上面那种方法将不可序列化的类型转为可序列化的类型进行序列化
# 那么反序列化时如何拿到序列化不到的类型呢
# 使用JSONDecode这个类
class MyDecoder(JSONDecoder):
def decode(self, s):
temp = json.loads(s)
# 针对特殊的键,进行反序列化
k8 = temp.pop("k8")
temp["k8"] = set(k8)
# print(temp["k8"],type(temp["k8"]))
k9 = temp.pop("k9")
temp["k9"] = datetime.strptime(k9, '%Y-%m-%d %H:%M:%S')
k10 = temp.pop("k10")
# print(k10)
temp["k10"] = Person(k10['name'], k10['age'])
return temp
obj_json = json.loads(str_json, cls=MyDecoder)
print(obj_json)
'''
小总结:
序列还就是将python数据类型转为字符串格式,方便数据进行保存
反序列化就是将字符串转为原本的数据类型
在python中有两种序列化方式,一个是json一个是pickle,
json是跨平台的,在任何语言中都能够使用
pickle只能在python中使用,pickle序列化后是看不都懂的二进制数据
这两个类都有一个方法是jumps(),这个方法就可以将数据类型转为字符串
还有一个方法是loads(),这个方法就可以将字符串转为相应的数据类型
并不是所有的类型都能够被序列化,比如列表,字符串,int,float,None,bool,元组,字典,都是可以序列化的,
但是集合,datetime, 一些类的示例都是不能够被序列化的,不能被序列化的数据会存放到JSONEncoder类的default方法的参数o中,
我们可以写一个类去继承JSONEncoder去重写其中的default方法,根据参数o去调整不能序列化的类型然后返回,这样这个不能序列化的类型就转为了一个可以序列化的类型
如果想反序列化为原本的类型,可以继承JSONDecoder这个类,重写其中的decode方法,根据特殊类型的键去调整其本来的类型,然后将调整过后的反序列化对象进行返回
python中有一个eval()函数,传入一个字符串,它的返回类型有列表,元组,字典,这也是反序列化的一种方式,但是这个方式并不安全,极不推荐
'''
10. 深浅拷贝
在Python中,浅拷贝(ShallowCopyQ)与深拷贝(Deep Copy)主要涉及复杂对象(如列表、字典等)的复制方式,这两种复制方式处理对象内部的子对象的方式不同。
浅拷贝创建一个新对象,其内容是原对象内各元素的引用。如果原对象包含的是不可变对象(如字符串、整数等),这种区别几乎察觉不到。但如果对象包含的是可变对象(如列表、字典等),修改新对象中的可变元素将影响到原对象,因为两者引用的是同一个可变元素。Python标准库中的copy模块提供的copy函数可以用来执行浅拷贝。
深拷贝不仅复制了原对象,而且递归地复制了对象内的所有可变元素及其包含的所有元素,因此原对象和新对象完全独立。对新对象的任何修改都不会影响原对象,反之亦然。这在处理包含复杂、嵌套对象的情况时非常有用。copy模块提供的deepcopy()函数用于执行深拷贝。
浅拷贝可以使用自身的copy方法,也可使用copy模块下的copy方法。拷贝的是对象内各元素的引用,如果对象内是可变数据类型,那么更改拷贝后的对象会连同原对象一起更改
深拷贝使用的是copy模块下的deepcopy方法,拷贝的是对象内各元素的值,无论对象内是什么类型的数据,更改其中一个对另一个完全没有影响。深拷贝出来的两个对象完全独立,互不影响
11. 装饰器
在不改变原函数功能的情况下为其增加新的功能。比如给某个函数增加一个运行时间开销统计的功能就能后使用到装饰器。
装饰器就是在需要增加功能的方法名上添加一个语法糖。@+闭包名字
说到装饰器就离不开闭包,闭包的本质还是函数,只不过这个函数可以为其他函数增加新的功能
闭包需要三个要素
- 外部函数嵌套内部函数
- 外部函数将内部函数名返回
- 内部函数可以使用外部函数的局部变量
有这三个要素的就是一个装饰器,就可以在想增加功能的函数上添加语法糖
12. 异步与同步
- 同步:当有多个程序运行的时候处于后面的程序需要等待前面的程序执行完毕,才能开始执行。也就是说当一个操作进行中,程序不能做其他的事情
- 异步:程序在发起一个耗时操作(文件读写,网络请求,数据库连接)后,不会等待其完成,而是继续执行后面的操作,也就是说程序在等待耗时操作的同时可以执行其他任务,从而提高了程序的并发性和相应能力
13. 元类(metaclass)
实例是由类创建的,而类是由元类创建的,目的是创建和加工一个类
可以使用type进行创建类
# 第一个参数是类的名字,第二个参数是需要继承的父类,第三个参数是类中的内容,比如类注释,类属性,类方法
name = type("name",(),{})
# 也可以在类后的小括号添加metaclass指定哪个元类来创建类
# 这样可以拦截类的构建过程,可以对类进行加工,默认为type
class Person(metaclass=MetaClass):
# 比如拦截类的创建过程,对不规范的用户名进行拦截,并修改
# 假设不规范的命名是:XxxXxxXxx
# 更改后的命名是:xxx_xxx_xxx
class MyType(type):
# 拦截类的创建过程
def __new__(cls, *args, **kwargs):
# print(args[2])
new_dict = {}
for k, v in args[2].items():
# print(k, v)
# 判断函数是不是魔法函数,是就不管它
if not k.startswith("__"):
new_key = ''
for i in range(len(k)):
# 判断大写字母,若有,将大写字母变为小写
if k[i].isupper():
# 在原本大写字母前加下划线,第一个若第一个字母时大写,则不加
if i != 0:
new_key += '_'
new_key += k[i].lower()
else:
# 构造修改好的函数名
new_key += k[i]
# 将原本的函数实现添加到修改后的函数名中
new_dict[new_key] = v
else:
# 将魔法函数的函数实现或者本来就规范的函数名的实现还给原本的函数名
new_dict[k] = v
# 构造新的构造函数的第三个参数
new_args = (args[0], args[1], new_dict)
# 调用构造函数实现类的创建
return super().__new__(cls, *new_args, **kwargs)
class Person(metaclass=MyType):
def SayHello(self):
print("hello")
def SayHi(self):
print("hi")
p = Person()
# p.SayHello()
p.say_hello()
p.say_hi()
14. 可迭代,迭代器,生成器的区别
- 可迭代就是具有遍历的功能。比如str,列表,元组,字典。其类的内部实现了
__iter__
方法 - 迭代器除了实现了
__iter__
方法之外还实现了__next__
方法,__iter__
方法返回迭代器自身,而__next__
方法返回容器的下一个元素,如果没有元素则抛出StopIteration异常 - 生成器是一种特殊的迭代器,通过在函数内部使用yield关键字创建,在使用yield关键字进行拆功创建后,其函数的返回值会作为异常的信息,当出现异常并打印异常信息时才会显示出来
- 还有一种生成器时元组推导式,元组推导式返回的结果是一个生成器
15. *args和**kwargs的区别
这两种都是定义可变长参数的形式
- *args用于接收位置参数,以元组的形式进行接收
- **kwargs用于接收关键字参数,以字典的形式进行接收
- 关键字参数在位置参数之后,所有*args必须在**kwar之前
16. is和==的区别
-
==是python中的比较运算符,用来比较两个对象的值于类型是否相等
-
is判断是两个对象的id是否相同
17. 如何漫游一个文件目录,有哪两种方法
-
用递归找
-
用os.walk返回生成器,生成器每一个元素都有三个部分,第一部分是当前目录,第二部分是当前目录下的子目录,第三部分是当前目录下的文件
# 1.使用os.walk漫游文件 # 返回的是一个生成器,里面低第一个存放的是当前路径,第二个存放的是子目录,第三个存放的是当前目录下的文件 import os # obj = os.walk("../pythonDemo") # for data in obj: # print(data[0],data[1],data[2]) # 2. 使用递归进行漫游 def traverse_dir(directory): for item in os.listdir(directory): # 将遍历的文件以路径的形式进行展现 new_path = os.path.join(directory, item) # print(new_path) if os.path.isdir(new_path): print(f"目录:{item}") traverse_dir(new_path) else: print(f"文件:{item}") traverse_dir("../pythonDemo")
18. 正则使用经验
正则:字符串匹配,搜索,替换模块
常用方法
- findall():返回列表
- search():返回Match或者None 获取内容使用group()
- match():是不是以指定规则作为开头,返回Match或者None
- fullmatch():是不是指定字符
19. IO读写
-
使用Open获取一个文件
-
Open("filepath",读写模式,编码方式)
-
常见的读写模式
- r:只读
- w:只写
- a:追加
- rb:读二进制文件
- wb:写二进制
20. 进程与线程
进程与线程都是用于提升程序效率的技术(并行执行)
-
进程:由操作系统分配资源和调度,每个进程之间都有自己的独立内存,一个进程默认有一个线程,进程的开销较大,但隔离性强,进程之间的数据默认是不共享(可以使用进程之间的Queue模块实现数据共享)
"""进程需要multiprocessing包,使用其中的Process(target='')方法用来创建进程. 无论时进程还是线程都需要在主方法main中进行启动. 可以使用join进行阻塞进程,阻塞是在进程或者线程开启之后才能进行""" # 进程之间通过queue实现数据共享 # 生产者,消费者模式 # 消费者 def p1_main(queue, name): while True: time.sleep(3) print(f"{name}进行消费:{queue.get()}") # 生产者 def p2_main(queue): while True: time.sleep(1) queue.put(random.randint(1, 100)) def main(): queue = Queue(maxsize=10) p1 = multiprocessing.Process(target=p1_main, args=(queue, 'p1')) p0 = multiprocessing.Process(target=p1_main, args=(queue, 'p0')) p3 = multiprocessing.Process(target=p1_main, args=(queue, 'p3')) p2 = multiprocessing.Process(target=p2_main, args=(queue,)) p1.start() p0.start() p3.start() p2.start() p1.join() p0.join() p3.join() p2.join() if __name__ == '__main__': main()
-
线程:是进程的一个执行单元,一个进程可以有多个线程,线程共享进程之间的资源,线程的崩溃可能会带来进程的崩溃,进程的开销小,线程之间的数据是共享的
- 优化:线程池,预先创建一定数量的线程或者进程放入池子,使用时在池子中获取,使用后放入池子,避免反复的创建与销毁
21. Async与await异步函数
- async定义一个异步函数,使其能够在事件循环中进行异步操作,可以通过await等待其他异步函数完成,不会阻断整个程序的执行
- await时,python会挂起当前的异步函数,执行其他的异步任务
22. 网络编程常见的通信协议
- http:超文本传输协议 用于web浏览器和web服务器之间传输数据的协议,它基于TCP协议
- https:安全超文本传输协议 https在http的基础上加入了 SSL/TLS 加密,用于保护数据在客户端和服务器之间的传输安全
- TCP:传输控制协议 是一种面向连接的,可靠的协议,它在通信双方之间建立可靠的连接,并提供数据传输的保证
- UDP:用户数据报协议 是一种面向无连接的,不可靠的协议,它不保存数据传输的可靠性和顺序性,但具有低延迟的特点
- FTP:文件传输协议 用于客户端和服务器之间传输文件,支持文件上传和下载操作
- SMTP:简单邮件传输协议 用于发送电子邮件
- pop3,IMAP:用于接收电子邮件