软件测试升职加薪必看系列:自动化测试Python进阶(九)

跨越移植陷阱:ESP-IDF下LVGL与ILI9341的组件化集成与常见避坑指南 本文详细解析了在ESP-IDF环境下将LVGL图形库与ILI9341显示屏驱动进行组件化集成的完整流程。重点介绍了移植过程中的关键配置策略、内存管理优化技巧以及常见问题解决方案,帮助开发者有效规避内存溢出、显示异常等典型陷阱,实现高效稳定的嵌入式GUI开发。 阅读详情

Python进阶9

Python socket编程

引言

sockets的历史悠久,它们最早在 1971 年的 APPANET 中使用,后来成为1983年发布的Berkeley Software Distribution(BSD)操作系统中的API,称为Berkeley sockets。

Web服务器和浏览器并不是使用sockets的唯一程序,各种规模和类型的客户端 - 服务器(client - server)应用程序也得到了广泛使用。

今天,尽管socket API使用的底层协议已经发展多年,而且已经有新协议出现,但是底层 API 仍然保持不变。

最常见的套接字应用程序类型是客户端 - 服务器(client - server)应用程序,其中一方充当服务器并等待来自客户端的连接。

Socket API介绍

Python中的socket模块提供了一个到Berkeley sockets API的接口,其中的主要接口函数如下:

  • socket()
  • bind()
  • listen()
  • accept()
  • connect()
  • connect_ex()
  • send()
  • recv()
  • close()

这些方便使用的接口函数和系统底层的功能调用相一致。

TCP Sockets

我们准备构建一个基于 TCP 协议的socket对象,为什么使用 TCP 呢,因为:

  • 可靠性:如果在传输过程中因为网络原因导致数据包丢失,会有相关机制检测到并且进行重新传输
  • 按序到达:一方发送到另一方的数据包是按发送顺序被接收的。

对比之下,UDP 协议是不提供这些保证的,但是它的响应效率更高,资源消耗更少。

TCP 协议并不需要我们自己去实现,在底层都已经实现好了,我们只需要使用Python的socket模块,进行协议指定就可以了。socket.SOCK_STREAM表示使用 TCP 协议,socket.SOCK_DGRAM表示使用 UDP 协议

我们来看看基于 TCP 协议socket的 API 调用和数据传送流程图,右边的一列是服务器端(server),左边的一列是客户端(client)。

image

要实现左边的处于监听状态的server,我们需要按照顺序调用这样几个函数:

  • socket(): 创建一个socket对象
  • bind(): 关联对应 ip 地址和端口号
  • listen(): 允许对象接收其他socket的连接
  • accept(): 接收其他socket的连接,返回一个元组(conn, addr),conn 是一个新的socket对象,代表这个连接,addr 是连接端的地址信息。

client调用connect()时,会通过 TCP 的三次握手,建立连接。当client连接到server时,server会调用accept()完成这次连接。

双方通过send()和recv()来接收和发送数据,最后通过close()来关闭这次连接,释放资源。一般server端是不关闭的,会继续等待其他的连接。

Echo Client and Server

刚才我们弄清楚了server和client使用socket进行通信的过程,我们现在要自己进行一个简单的也是经典的实现:server复述从client接收的信息。

Echo Server

import socket

HOST = '127.0.0.1'  # Standard loopback interface address (localhost)
PORT = 65431       # Port to listen on (non-privileged ports are > 1023)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

socket.socket()创建了一个socket对象,它实现了上下文管理器协议,我们直接用 with 语句进行创建即可,而且最后不需要调用close()函数。

socket()中的两个参数指明了连接需要的 ip地址类型和传输协议类型,socket.AF_INET 表示使用 IPv4的地址进行连接,socket.SOCK_STREAM 表示使用 TCP 协议进行数据的传输。

bind()用来将socket对象和特定的网络对象和端口号进行关联,函数中的两个参数是由创建socket对象时指定的 ip地址类型 决定的,这里使用的是socket.AF_INET(IPv4),因此,bind()函数接收一个元组对象作为参数(HOST, PORT)

  • host可以是一个主机名,IP地址,或者空字符串。如果使用的是 IP地址,host必须是 IPv4格式的地址字符串。127.0.0.1是本地环路的标准写法,因此只有在主机上的进程才能够连接到server,如果设置为空字符串,它可以接受所有合法 IPv4地址的连接。

  • port应该是从1 - 65535的一个整数(0被保留了),它相当于是一个窗口和其他的客户端建立连接,如果想使用1 - 1024的端口,一些系统可能会要求要有管理员权限。

listen()使得server可以接受连接,它可以接受一个参数:backlog,用来指明系统可以接受的连接数量,虽然同一时刻只能与一端建立连接,但是其他的连接请求可以被放入等待队列中,当前面的连接断开,后面的请求会依次被处理,超过这个数量的连接请求再次发起后,会被server直接拒绝。

从Python 3.5开始,这个参数是可选的,如果我们不明确指明,它就采用系统默认值。如果server端在同一时刻会收到大量的连接请求,通常要把这个值调大一些,在Linux中,可以在/proc/sys/net/core/somaxconn看到值的情况,详细请参阅:

accept()监听连接的建立,是一个阻塞式调用,当有client连接之后,它会返回一个代表这个连接的新的socket对象和代表client地址信息的元组。对于 IPv4 的地址连接,地址信息是 (host, port),对于 IPv6 ,(host, port, flowinfo, scopeid)

有一件事情需要特别注意,accept()之后,我们获得了一个新的socket对象,它和server以及client都不同,我们用它来进行和client的通信。

conn, addr = s.accept()
with conn:
    print('Connected by', addr)
    while True:
        data = conn.recv(1024)
        if not data:
            break
        conn.sendall(data)

conn是我们新获得的socket对象,conn.recv()也是一个阻塞式调用,它会等待底层的 I/O 响应,直到获得数据才继续向下执行。外面的while循环保证server端一直监听,通过conn.sendall将数据再发送回去。

Echo Client

import socket

HOST = '127.0.0.1'  # The server's hostname or IP address
PORT = 65431        # The port used by the server

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall("Hello, world".encode("utf8"))
    data = s.recv(1024)

print('Received', data.decode("utf8"))

和server相比,client更加简单,先是创建了一个socket对象,然后将它和server连接,通过s.sendall()将信息发送给server,通过s.recv()获得来自server的数据,然后将其打印输出。

在发送数据时,只支持发送字节型数据,所以我们要将需要发送的数据进行编码,在收到server端的回应后,将得到的数据进行解码,就能还原出我们能够识别的字符串了。

启动程序

我们要先启动server端,做好监听准备,然后再启动client端,进行连接。

这个信息是在client连接后打印出来的。

image

 

image

可以使用netstat这个命令查看socket的状态,更详细使用可以查阅帮助文档。

查看系统中处于监听状态的socket,过滤出了使用 TCP协议 和 IPv4 地址的对象:

image

如果先启动了client,会有下面这个经典的错误:

image

造成的原因可能是端口号写错了,或者server根本就没运行,也可能是在server端存在防火墙阻值了连接建立,下面是一些常见的错误异常:

Exceptionerrno ConstantDescription
BlockingIOErrorEWOULDBLOCKResource temporarily unavailable. For example, in non-blocking mode, when calling send() and the peer is busy and not reading, the send queue (network buffer) is full. Or there are issues with the network. Hopefully this is a temporary condition.
OSErrorADDRINUSEAddress already in use. Make sure there’s not another process running that’s using the same port number and your server is setting the socket option SO_REUSEADDR: socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1).
ConnectionResetErrorECONNRESETConnection reset by peer. The remote process crashed or did not close its socket properly (unclean shutdown). Or there’s a firewall or other device in the network path that’s missing rules or misbehaving.
TimeoutErrorETIMEDOUTOperation timed out. No response from peer.
ConnectionRefusedErrorECONNREFUSEDConnection refused. No application listening on specified port.

连接的建立

现在我们仔细看一下server和client是怎样建通信的:

image

当使用环路网络((IPv4 address 127.0.0.1 or IPv6 address ::1))的时候,数据没有离开过主机跑到外部的网络。如图所示,环路网络是在主机内部建立的,数据就经过它来发送,从主机上运行的一个程序发送到另一个程序,从主机发到主机。这就是为什么我们喜欢说环路网络和 IP地址 127.0.0.1(IPv4) 或 ::1(IPv6) 都表示主机

如果server使用的时其他的合法IP地址,它就会通过以太网接口与外部网络建立联系:

image

如何处理多端连接

echo server最大的缺点就是它同一时间只能服务一个client,直到连接的断开,echo client同样也有不足,当client进行如下操作时,有可能s.recv()只返回了一个字节的数据,数据并不完整。

data = s.recv(1024)

这里所设定的参数 1024 表示单次接收的最大数据量,并不是说会返回 1024 字节的数据。在server中使用的send()与之类似,调用后它有一个返回值,标示已经发送出去的数据量,可能是小于我们实际要发送的数据量,比如说有 6666 字节的数据要发送,用上面的发送方式要发送很多此才行,也就是说一次调用send()数据并没有被完整发送,我们需要自己做这个检查来确保数据完整发送了。

因此,这里使用了sendall(),它会不断地帮我们发送数据直到数据全部发送或者出现错误。

所以,目前有两个问题:

  • 怎样同时处理多个连接?
  • 怎样调用send()和recv()直到数据全部发送或接收。

要实现并发,传统方法是使用多线程,最近比较流行的方法是使用在Python3.4中引入的异步IO模块asyncio。

这里准备用更加传统,但是更容易理解的方式来实现,基于系统底层的一个调用:select(),Python中也提供了对应的模块:selectors ,它在原生的实现上进行了封装,通过使用DefaultSelector,能更加简单的完成任务。

select()通过了一种机制,它来监听操作发生情况,一旦某个操作准备就绪(一般是读就绪或者是写就绪),然后将需要进行这些操作的应用程序select出来,进行相应的读和写操作。到这里,你可能会发现这并没有实现并发,但是它的响应速度非常快,通过异步操作,足够模拟并发的效果了。

Muti-Connection Client and Server

Multi-Connection Server

import selectors

sel = selectors.DefaultSelector()
# ...
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
lsock.bind((host, port))
lsock.listen()
print('listening on', (host, port))
lsock.setblocking(False)
sel.register(lsock, selectors.EVENT_READ, data=None)

和echo server最大的不同就在于,通过lsock.setblocking(False),将这个socket对象设置成了非阻塞形式,与sel.select()一起使用,就可以在一个或多个socket对象上等待事件,然后在数据准备就绪时进行数据的读写操作。

sel.register()给server注册了我们需要的事件,对server来说,我们需要 I/O 可读,从而进行client发送数据的读入,因此,通过selector.EVENT_READ来指明。

data用来存储和socket有关的任何数据,当sel.select()返回结果时,它也被返回,我们用它作为一个标志,来追踪拥有读入和写入操作的socket对象。

接下来是事件循环:

import selectors
sel = selectors.DefaultSelector()

# ...

while True:
    events = sel.select(timeout=None)
    for key, mask in events:
        if key.data is None:
            accept_wrapper(key.fileobj)
        else:
            service_connection(key, mask)

sel.select(timeout=None)是一个阻塞式调用,直到有socket对象准备好了 I/O 操作,或者等待时间超过设定的timeout。它将返回(key, events)这类元组构成的一个列表,每一个对应一个就绪的socket对象。

key是一个SeletorKey类型的实例,它有一个fileobj的属性,这个属性就是sokect对象。

mask是就绪操作的状态掩码。

如果key.data is None,我们就知道,这是一个server对象,于是要调用accept()方法,用来等待client的连接。不过我们要调用我们自己的accept_wrapper()函数,里面还会包含其他的逻辑。

如果key.data is not None,我们就知道,这是一个client对象,它带着数据来建立连接啦!然后我们要为它提供服务,于是就调用service_connection(key, mask),完成所有的服务逻辑。

def accept_wrapper(sock):
    conn, addr = sock.accept()  # Should be ready to read
    print('accepted connection from', addr)
    conn.setblocking(False)
    data = types.SimpleNamespace(addr=addr, inb=b'', outb=b'')
    events = selectors.EVENT_READ | selectors.EVENT_WRITE
    sel.register(conn, events, data=data)

这个函数用来处理与client的连接,使用conn.setblocking(False)将该对象设置为非阻塞状态,这正是我们在这个版本的程序中所需要的,否则,整个server会停止,直到它返回,这意味着其他socket对象进入等待状态。

然后,使用types.SimplleNamespace()构建了一个data对象,存储我们想保存的数据和socket对象。

因为数据的读写都是通过conn,所以使用selectors.EVENT_READ | selectors.EVENT_WRITE,然后用sel.register(conn, events, data=data)进行注册。

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)  # Should be ready to read
        if recv_data:
            data.outb += recv_data
        else:
            print('closing connection to', data.addr)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        if data.outb:
            print('echoing', repr(data.outb), 'to', data.addr)
            sent = sock.send(data.outb)  # Should be ready to write
            data.outb = data.outb[sent:]

这就时服务逻辑的核心,key中包含了socket对象和data对象,mask是已经就绪操作的掩码。根据sock可以读,将数据保存在data.outb中,这也将成为写出的数据。

if recv_data:
    data.outb += recv_data
else:
    print('closing connection to', data.addr)
    sel.unregister(sock)
    sock.close()

如果没有接收到数据,说明client数据发完了,sock的状态不再被追踪,然后关闭这次连接。

Multi-Connection Client

messages = [b'Message 1 from client.', b'Message 2 from client.']


def start_connections(host, port, num_conns):
    server_addr = (host, port)
    for i in range(0, num_conns):
        connid = i + 1
        print('starting connection', connid, 'to', server_addr)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(False)
        sock.connect_ex(server_addr)
        events = selectors.EVENT_READ | selectors.EVENT_WRITE
        data = types.SimpleNamespace(connid=connid,
                                     msg_total=sum(len(m) for m in messages),
                                     recv_total=0,
                                     messages=list(messages),
                                     outb=b'')
        sel.register(sock, events, data=data)

使用connect_ex()而不是connect(),因为connect()会立即引发BlockingIOError异常。connect_ex()只返回错误码 errno.EINPROGRESS,而不是在连接正在进行时引发异常。连接完成后,socket对象就可以进行读写,并通过select()返回。

连接建立完成后,我们使用了types.SimpleNamespace构建出和socket对象一同保存的数据,里面的messages对我们要发送的数据做了一个拷贝,因为在后续的发送过程中,它会被修改。client需要发送什么,已经发送了什么以及已经接收了什么都要进行追踪,总共要发送的数据字节数也保存在了data对象中。

def service_connection(key, mask):
    sock = key.fileobj
    data = key.data
    if mask & selectors.EVENT_READ:
        recv_data = sock.recv(1024)  # Should be ready to read
        if recv_data:
            print('received', repr(recv_data), 'from connection', data.connid)
            data.recv_total += len(recv_data)
        if not recv_data or data.recv_total == data.msg_total:
            print('closing connection', data.connid)
            sel.unregister(sock)
            sock.close()
    if mask & selectors.EVENT_WRITE:
        if not data.outb and data.messages:
            data.outb = data.messages.pop(0)
        if data.outb:
            print('sending', repr(data.outb), 'to connection', data.connid)
            sent = sock.send(data.outb)  # Should be ready to write
            data.outb = data.outb[sent:]

client要追踪来自server的数据字节数,如果收到的数据字节数和发送的相等,或者有一次没有收到数据,说明数据接收完成,本次服务目的已经达成,就可以关闭这次连接了。

data.outb用来维护发送的数据,前面提到过,一次发送不一定能将数据全部送出,使用data.outb = data.outb[sent:]来更新数据的发送。发送完毕后,再messages中取出数据准备再次发送。

最后的运行效果如下:

image

image

还是要先启动server,进入监听状态,然后client启动,与server建立两条连接,要发送的信息有两条,这里分开发送,先将fist message分别发送到server,然后再发送second message。server端收到信息后进行暂时保存,当两条信息都收到了才开始进行echo,client端收到完整信息后表示服务结束,断开连接。

如果大家对于学习Python有任何问题,学习方法,学习路线,如何学习有效率的问题,可以随时来咨询我,或者缺少系统学习资料的,我做这行年头比较久,自认为还是比较有经验的,可以帮助大家提出建设性建议,这是我的Python交流qun:785128166,有任何问题可以随时来咨询我。
 

计算机视觉:AI生成内容(AIGC)对视觉领域的冲击 计算机视觉:AI生成内容(AIGC)对视觉领域的冲击​ 人工智能,计算机视觉,大模型,AI,在信息技术日新月异的当下,计算机视觉作为人工智能领域的关键分支,已经深度融入了我们生活与工作的各个层面。从安防监控系统里精准的人脸识别,助力维护公共安全;到工业生产线上高效的产品质量检测,保障产品品质;再到医疗影像分析中为疾病诊断提供有力支持,计算机视觉技术凭借其对视觉信息的强大处理与理解能力,不断推动着各行业的发展与变革。 阅读详情

相关推荐

UE5 Metahuman与Blender手动绑定:ARKit面部动画实战对比

面部动画是角色动画领域的核心技术,其核心原理在于通过驱动面部骨骼或混合形状来模拟真实肌肉运动,从而赋予数字角色生命力。这项技术的价值在于极大地提升了虚拟角色的表现力与沉浸感,广泛应用于游戏开发、影视制作、虚拟现实和实时交互应用。在技术实现路径上,开发者常面临工业化集成方案与高度自定义方案的选择。本文聚焦于使用行业标准的ARKit 52个面部混合形状数据,深度对比了虚幻引擎Metahuman的工业化流程与Blender手动绑定方案在视觉保真度、工作流效率、性能开销和扩展性上的差异,为不同项目规模与技术需求的团

weixin_33901926的博客 449

Python Web自动化测试

工欲善其事必先利其器,废话不多说。我们先开始搭建环境。

软件自动化测试技术交流、资源分享 2160

51单片机基础知识

前言 周末闲着无聊,以后定时更新一期关于51单片机的学习案例,以供大家平时参考使用。至于单片机,你可以理解为人的大脑,而人各项身体器官的运作需要意识给到大脑,然后大脑再驱动我们的中枢神经进行相应的动作,这里的意识可以理解为我们要写的程序,而人的器官相当于各种输出设备,例如我们以后要学的LED、电机、各种传感器等等。学好单片机能让你找到一份相对不错的工作,当然越可观的东西也是要付出相应努力的。 一、51单片机是什么? 通常我们一说到 51 单片机,指的都是兼容IntelMCS-51体系架构的一系列单片机,而

qq_43537819的博客 6903

2024年Selenium+Python做web端自动化测试框架与实例详解教程(1),2024年最新2024-2024历年华为跳动软件测试面试真题解析

现在就从上往下结束吧!

2401_84558983的博客 361

汽车诊断服务(UDS——0x11服务解析)

ECU复位服务(0x11)详解:该服务支持硬件/钥匙开关/软件三种复位类型(0x01-0x03),复位前需发送肯定响应。请求格式为11+子功能,响应含51+子功能。典型应用需配合安全访问流程,测试时需监控CAN总线通信及DTC状态。复位后ECU将返回默认会话,期间不处理任何诊断请求。服务在编程会话且安全解锁后执行,硬复位会初始化存储数据。

从事汽车行业 979

逆变器母线电容选型与均压电阻设计实战指南

本文为逆变器母线电容选型与均压电阻设计提供了实战指南。文章从工程角度出发,详细解析了母线电容耐压值、容值的计算方法,并重点强调了纹波电流热设计的重要性。同时,深入探讨了串联电容均压电阻的设计原则与功率计算,通过5.5kW变频器案例,系统演示了从参数确定到最终选型的完整流程,旨在帮助工程师设计出稳定可靠的逆变器系统。

weixin_29299807的博客 291

Web自动化测试学习(入门笔记)

Web自动化测试 学习感悟

m0_52351604的博客 391

【ESP32使用MAX98357播放音频】

在物联网和智能家居领域,音频播放功能越来越受到重视。ESP32作为一款功能强大的微控制器,结合MAX98357音频放大器模块,可以实现高品质的音频播放。本文将详细介绍如何使用ESP32的I2S接口通过MAX98357进行音频播放。

vor234的博客 7526

【自动化测试基础】二、python高级知识

上一篇:一、python基础知识 文章目录十二、高级内置函数 TODO1. eval():能够识别python中字符串里的python表达式,并进行计算2. exec()识别str字符串的时候,识别到fun1()的函数,3. filter4. map5. zip6. isinstance 十二、高级内置函数 TODO 1. eval():能够识别python中字符串里的python表达式,并进行计算 str1 = "abc" str2 = "1+2" str3 = 1 str4 = 2 print(.

你好我是例子,哦不!是栗子 943

X书x-mini-sig_x-mini-mua

X书的tiny风控代码,成功运行x-mini-sig|x-mini-mua

Codeooo 博客 1万+

SCADA|KingSCADA通过组合框选择修改变量的值

《SCADA项目中组合框控件的使用方法》 本文详细介绍了SCADA系统中组合框(下拉框)控件的应用方法。通过具体示例演示如何实现:当选择"雷工笔记"时变量值为100,选择"雷工杂谈"时为200,选择"小李食代"时为300的功能。主要实现步骤包括:1)创建项目并添加组合框控件;2)设置Items选项及对应数值;3)创建内存变量;4)使用GetCurrentIndex()和GetItemData()方法;5)在选项变化事件中编写脚本。该控件在SCADA

做有价值的事,积累有价值的笔记! 863

软件测试升职加薪必看系列:自动化测试Python进阶(六)

Python进阶6 对象引用、垃圾回收、可变性 Python中的变量是什么 引言 Python和java中的变量本质不一样,java的变量可以理解为一个盒子,用来容纳我们的对象,使用前要先声明它,好分配给我们合适的内存空间。Python的变量可以理解为一个标签,先构造出对象,再将变量贴在对象上。 Python变量没有类型,对象才有,本质上是指针。同一个变量,可以表示不同对象。 使用案例 """ a 和 b 都贴在了同一个对象上 """ a = [1, 2] b = a a.append(6

测试小小小的博客 266

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

软件测试升职加薪必看系列:自动化测试Python进阶(一)

Python进阶1 Python中一切皆对象 引言 Java语言也是面向对象的语言,但是Python要更加彻底 Python的面向对象特性,是它使用起来灵活的根本所在 对象的特点 可以赋值给一个变量 # 函数也是对象 def test(name): print(name) my_func = test # 注意 只写函数名 和 函数名加括号 的区别 my_func("MetaTian") # 打印:MetaTian 可以添加到集合中去 def plus

测试小小小的博客 261

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

软件测试升职加薪必看系列:自动化测试Python进阶(三)

Python进阶3 深入类和对象 鸭子类型和多态 引言 在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。 例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为鸭子的对象,并调用它的走和叫方法。 在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名...

测试小小小的博客 320

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

软件测试升职加薪必看系列:自动化测试Python进阶(二)

Python进阶2 魔法函数 引言 魔法函数是Python中定义的,以__开头,__结尾,形如__func__()的函数,一般使用已经存在的魔法函数即可。 使用这样一些函数,可以让我们自定义的类有更加强大的特性。 魔法函数是隐式调用的,不需要我们显示调用。 class Language(object): def __init__(self, language_list): self.lans = language_list # 如果要遍历所有的语言,可以:

测试小小小的博客 332

紧急通知,错过这次机会,升职加薪将等到三个月之后

俗话说:"不积跬步无以至千里,不积小流无以成江海”,坚持做正向的事情,慢慢积累,你当下的每一步都是未来的基石和铺垫。在软件测试的职业生涯中也是如此,在工作中坚持做正向积累,你终会到达你想要的那个目标!

YJT1002的博客 884

软件测试升职加薪必看系列:自动化测试Python进阶(十)

Python进阶10 多线程 python中的GIL GIL(Global Interpreter Lock),就是一个锁。 Python中的一个线程对应于 C语言 中的一个线程。 GIL使得同一时刻只有一个线程在一个cpu上执行字节码,无法将多个线程分配到多个cpu上进行同步运行。如果在单核cpu上,线程是并发运行,而不是并行。 image 首先,这样效率不高,但是看似也不会产生数据访问冲突的问题,毕竟同一时刻只有一个线程在一个核上运行嘛,然而: sum = 0 def add(

测试小小小的博客 380

软件测试升职加薪必看系列:自动化测试Python进阶(七)

Python进阶7 元类编程 Python属性函数 引言 Python中我们对于自己设置的类,尽量要使其属性私有化,获得更好的封装性。 如果要访问和修改私有属性,要为其设置set和get方法。 Python中,可以使用特殊的装饰器将set和get方法属性化,这样就能够使用更简洁的语法去调用这些方法。 使用案例 class Person: def __init__(self, name, age): self.__name = name self._

测试小小小的博客 334

软件测试升职加薪必看系列:自动化测试Python进阶(五)

Python进阶5 深入Python的set和dict dict常用操作 引言 clear(): 清空字典 copy(): 返回一个浅拷贝 fromkeys(): 将可迭代对象中的每一个元素作为key和同一个value拼成字典 get(): 根据key返回value,若无对应的键值对,则返回None,也可以指定默认返回值,和索引访问相比,不会产生异常。 items():返回一个dict_items类型,支持迭代,键值对以元组形式组织 setdefault(): 获取key对应的valu

测试小小小的博客 395

软件测试升职加薪必看系列:自动化测试Python进阶(四)

Python进阶4 自定义序列类型 序列类型的分类 按照序列存储数据的类型 容器序列:list, tuple, deque 扁平序列:str, bytes, array.array, bytearray 按照序列是否可变 可变序列:list, deque, array.array, bytearray 不可变序列:str, tuple, bytes 序列的abc继承关系 引言 collections.abc模块中有很多内置的抽象基类 序列主要和两个抽象基类有关:Sequence和..

测试小小小的博客 354

软件测试升职加薪必看系列:自动化测试Python进阶(八)

Python进阶8 迭代器和生成器 python的迭代协议 引言 迭代器是访问集合内部元素的一种方式,一般用来遍历数据。 迭代器和用下标索引访问的方式不一样,迭代器是不能返回值的 迭代器提供了一种惰性访问数据的方式,需要的时候才产生数据。 可迭代类型都实现了迭代协议,实际上就是__iter__()这个魔法函数。 可迭代类型和迭代器 前面讲过,collections.abc模块中定义了很多内置的抽象基类,现在我们重点关注其中的两个:Iterable和Iterator Iterable..

测试小小小的博客 284

❤️程序人生 | 为什么越来越多的人用Python来做自动化测试?

这段时间网上很流行一句话: 让自己变得更好,才是解决一切问题的关键。 也许你还从来没想过: 如果现在失业,你的存款能够支撑你多久的日常生活,它能应付哪种级别的风险,例如失业、破产、裁员、意外…… 其实生活中有很多“照妖镜”,让你能清楚地看到自己有几分抗风险能力。 可能一场疫情让你处于两难的境地,谁又能保证明天会不会发生些什么。很多时候,你处于岁月静好的生活中,其实暗流涌动,崩塌只在旦夕之间。 而一个真正强大的人,是永远不会停下学习这件事的。因为他知道,落后就意味着被淘汰,这是永恒不变.

2354

学了就能升职加薪?大厂软件测试面试必问的自动化测试框架你真会了吗?

最近我注意到了一个现象,就是每年一到3、4月左右的时候,就会有一大批粉丝朋友来咨询我跳槽、面试这块的问题。

软件自动化测试技术交流、资源分享 101

大厂软件测试面试必问的“自动化测试框架”你真的会了吗?功能测试人备战2022升职加薪最新攻略

做测试这几年来,我注意到了一个现象,就是每年一到12月份左右的时候,就会有一大批粉丝朋友来咨询我跳槽、面试技巧等这方面的问题。

软件自动化测试技术交流、资源分享 193

不想一直“点点点”,软件测试涨薪必看

软件测试岗位作为互联网行业其中分支之一,是软件公司不可或缺的岗位,但是从事过软件测试行业的朋友就清楚,软件测试涨薪是相对比较难的,下面这篇文章就来告诉大家,软件测试岗位如何提高自己的薪资! 软件测试的入门是比较简单的,但是想要做到中高级,还是有很长的一段路要走,想要加薪,不止要会功能测试,还要会性能测试和自动化测试…【看完有惊喜哦】 作为还在功能测试岗位上徘徊的工程师,怎样才能让自己的薪资翻倍,成为软件测试的全能人才呢? 第一个阶段:软件测试的理论基础和工具【薪资:4k-8k】 熟悉软件的原理、架构 熟悉

qq_40214204的博客 2128
上一篇: 软件测试升职加薪必看系列:自动化测试Python进阶(八)
下一篇: 软件测试升职加薪必看系列:自动化测试Python进阶(十)
测试小小小
博客等级 码龄6年 265粉丝 364原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值