亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

python socket網(wǎng)絡(luò)編程之粘包問題詳解

 更新時(shí)間:2018年04月28日 11:50:02   作者:戰(zhàn)爭熱誠  
這篇文章主要介紹了python socket網(wǎng)絡(luò)編程之粘包問題詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

一,粘包問題詳情

1,只有TCP有粘包現(xiàn)象,UDP永遠(yuǎn)不會(huì)粘包

你的程序?qū)嶋H上無權(quán)直接操作網(wǎng)卡的,你操作網(wǎng)卡都是通過操作系統(tǒng)給用戶程序暴露出來的接口,那每次你的程序要給遠(yuǎn)程發(fā)數(shù)據(jù)時(shí),其實(shí)是先把數(shù)據(jù)從用戶態(tài)copy到內(nèi)核態(tài),這樣的操作是耗資源和時(shí)間的,頻繁的在內(nèi)核態(tài)和用戶態(tài)之前交換數(shù)據(jù)勢必會(huì)導(dǎo)致發(fā)送效率降低, 因此socket 為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一次數(shù)據(jù)給對方。若連續(xù)幾次需要send的數(shù)據(jù)都很少,通常TCP socket 會(huì)根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一個(gè)TCP段后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。

2,首先需要掌握一個(gè)socket收發(fā)消息的原理

發(fā)送端可以是1k,1k的發(fā)送數(shù)據(jù)而接受端的應(yīng)用程序可以2k,2k的提取數(shù)據(jù),當(dāng)然也有可能是3k或者多k提取數(shù)據(jù),也就是說,應(yīng)用程序是不可見的,因此TCP協(xié)議是面來那個(gè)流的協(xié)議,這也是容易出現(xiàn)粘包的原因而UDP是面向無連接的協(xié)議,每個(gè)UDP段都是一條消息,應(yīng)用程序必須以消息為單位提取數(shù)據(jù),不能一次提取任一字節(jié)的數(shù)據(jù),這一點(diǎn)和TCP是很同的。怎樣定義消息呢?認(rèn)為對方一次性write/send的數(shù)據(jù)為一個(gè)消息,需要命的是當(dāng)對方send一條信息的時(shí)候,無論鼎城怎么樣分段分片,TCP協(xié)議層會(huì)把構(gòu)成整條消息的數(shù)據(jù)段排序完成后才呈現(xiàn)在內(nèi)核緩沖區(qū)。    

例如基于TCP的套接字客戶端往服務(wù)器端上傳文件,發(fā)送時(shí)文件內(nèi)容是按照一段一段的字節(jié)流發(fā)送的,在接收方看來更笨不知道文件的字節(jié)流從何初開始,在何處結(jié)束。

3,粘包的原因

3-1 直接原因

所謂粘包問題主要還是因?yàn)榻邮辗讲恢老⒅g的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)所造成的

3-2  根本原因

發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一個(gè)TCP段。若連續(xù)幾次需要send的數(shù)據(jù)都很少,通常TCP會(huì)根據(jù) 優(yōu)化算法 把這些數(shù)據(jù)合成一個(gè)TCP段后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。

3-3 總結(jié)

  1.  TCP(transport control protocol,傳輸控制協(xié)議)是面向連接的,面向流的,提供高可靠性服務(wù)。收發(fā)兩端(客戶端和服務(wù)器端)都要有一一成對的socket,因此,發(fā)送端為了將多個(gè)發(fā)往接收端的包,更有效的發(fā)到對方,使用了優(yōu)化方法(Nagle算法),將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù),合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包。這樣,接收端,就難于分辨出來了,必須提供科學(xué)的拆包機(jī)制。 即面向流的通信是無消息保護(hù)邊界的。
  2. UDP(user datagram protocol,用戶數(shù)據(jù)報(bào)協(xié)議)是無連接的,面向消息的,提供高效率服務(wù)。不會(huì)使用塊的合并優(yōu)化算法,, 由于UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區(qū))采用了鏈?zhǔn)浇Y(jié)構(gòu)來記錄每一個(gè)到達(dá)的UDP包,在每個(gè)UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對于接收端來說,就容易進(jìn)行區(qū)分處理了。  即面向消息的通信是有消息保護(hù)邊界的。
  3. tcp是基于數(shù)據(jù)流的,于是收發(fā)的消息不能為空,這就需要在客戶端和服務(wù)端都添加空消息的處理機(jī)制,防止程序卡住,而udp是基于數(shù)據(jù)報(bào)的,即便是你輸入的是空內(nèi)容(直接回車),那也不是空消息,udp協(xié)議會(huì)幫你封裝上消息頭,實(shí)驗(yàn)略

udp的recvfrom是阻塞的,一個(gè)recvfrom(x)必須對唯一一個(gè)sendinto(y),收完了x個(gè)字節(jié)的數(shù)據(jù)就算完成,若是y>x數(shù)據(jù)就丟失,這意味著udp根本不會(huì)粘包,但是會(huì)丟數(shù)據(jù),不可靠

tcp的協(xié)議數(shù)據(jù)不會(huì)丟,沒有收完包,下次接收,會(huì)繼續(xù)上次繼續(xù)接收,己端總是在收到ack時(shí)才會(huì)清除緩沖區(qū)內(nèi)容。數(shù)據(jù)是可靠的,但是會(huì)粘包。

二,兩種情況下會(huì)發(fā)生粘包:

1,發(fā)送端需要等到本機(jī)的緩沖區(qū)滿了以后才發(fā)出去,造成粘包(發(fā)送數(shù)據(jù)時(shí)間間隔很短,數(shù)據(jù)很小,python使用了優(yōu)化算法,合在一起,產(chǎn)生粘包)

客戶端

#_*_coding:utf-8_*_
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
s.send('hello'.encode('utf-8'))
s.send('feng'.encode('utf-8'))

服務(wù)端

#_*_coding:utf-8_*_
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(10)
data2=conn.recv(10)
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()

2,接收端不及時(shí)接受緩沖區(qū)的包,造成多個(gè)包接受(客戶端發(fā)送一段數(shù)據(jù),服務(wù)端只收了一小部分,服務(wù)端下次再收的時(shí)候還是從緩沖區(qū)拿上次遺留的數(shù)據(jù),就產(chǎn)生粘包) 客戶端

#_*_coding:utf-8_*_
import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)
s.send('hello feng'.encode('utf-8'))

服務(wù)端

#_*_coding:utf-8_*_
from socket import *
ip_port=('127.0.0.1',8080)
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
conn,addr=tcp_socket_server.accept()
data1=conn.recv(2) #一次沒有收完整
data2=conn.recv(10)#下次收的時(shí)候,會(huì)先取舊的數(shù)據(jù),然后取新的
print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))
conn.close()

三,粘包實(shí)例:

服務(wù)端

import socket
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.bind(ip_port)
din.listen(5)
conn,deer=din.accept()
data1=conn.recv(1024)
data2=conn.recv(1024)
print(data1)
print(data2)

客戶端:

import socket
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.connect(ip_port)
din.send('helloworld'.encode('utf-8'))
din.send('sb'.encode('utf-8'))

四,拆包的發(fā)生情況

當(dāng)發(fā)送端緩沖區(qū)的長度大于網(wǎng)卡的MTU時(shí),tcp會(huì)將這次發(fā)送的數(shù)據(jù)拆成幾個(gè)數(shù)據(jù)包發(fā)送過去

補(bǔ)充問題一:為何tcp是可靠傳輸,udp是不可靠傳輸

關(guān)于tcp傳輸請參考: http://chabaoo.cn/network/176867.html

tcp在數(shù)據(jù)傳輸時(shí),發(fā)送端先把數(shù)據(jù)發(fā)送到自己的緩存中,然后協(xié)議控制將緩存中的數(shù)據(jù)發(fā)往對端,對端返回一個(gè)ack=1,發(fā)送端則清理緩存中的數(shù)據(jù),對端返回ack=0,則重新發(fā)送數(shù)據(jù),所以tcp是可靠的

而udp發(fā)送數(shù)據(jù),對端是不會(huì)返回確認(rèn)信息的,因此不可靠

補(bǔ)充問題二:send(字節(jié)流)和recv(1024)及sendall是什么意思?

recv里指定的1024意思是從緩存里一次拿出1024個(gè)字節(jié)的數(shù)據(jù)

send的字節(jié)流是先放入己端緩存,然后由協(xié)議控制將緩存內(nèi)容發(fā)往對端,如果字節(jié)流大小大于緩存剩余空間,那么數(shù)據(jù)丟失,用sendall就會(huì)循環(huán)調(diào)用send,數(shù)據(jù)不會(huì)丟失。

五,粘包問題如何解決?

問題的根源在于,接收端不知道發(fā)送端將要傳送的字節(jié)流的長度,所以解決粘包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來一個(gè)死循環(huán)接收完所有數(shù)據(jù)。

 5-1  簡單的解決方法(從表面解決):

在客戶端發(fā)送下邊添加一個(gè)時(shí)間睡眠,就可以避免粘包現(xiàn)象。在服務(wù)端接收的時(shí)候也要進(jìn)行時(shí)間睡眠,才能有效的避免粘包情況。

客戶端:

#客戶端
import socket
import time
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.connect(ip_port)
din.send('helloworld'.encode('utf-8'))
time.sleep(3)
din.send('sb'.encode('utf-8'))

服務(wù)端:

#服務(wù)端
import socket
import time
import subprocess
din=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=('127.0.0.1',8080)
din.bind(ip_port)
din.listen(5)
conn,deer=din.accept()
data1=conn.recv(1024)
time.sleep(4)
data2=conn.recv(1024)
print(data1)
print(data2)

上面解決方法肯定會(huì)出現(xiàn)很多紕漏,因?yàn)槟悴恢朗裁磿r(shí)候傳輸完,時(shí)間暫停的長短都會(huì)有問題,長的話效率低,短的話不合適,所以這種方法是不合適的。

5-2 普通的解決方法(從根本看問題):

問題的根源在于,接收端不知道發(fā)送端將要傳送的字節(jié)流的長度,所以解決粘包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來一個(gè)死循環(huán)接收完所有數(shù)據(jù)

為字節(jié)流加上自定義固定長度報(bào)頭,報(bào)頭中包含字節(jié)流長度,然后依次send到對端,對端在接受時(shí),先從緩存中取出定長的報(bào)頭,然后再取真是數(shù)據(jù)。

使用struct模塊對打包的長度為固定4個(gè)字節(jié)或者八個(gè)字節(jié),struct.pack.format參數(shù)是“i”時(shí),只能打包長度為10的數(shù)字,那么還可以先將長度轉(zhuǎn)化為json字符串,再打包。

普通的客戶端

# _*_ coding: utf-8 _*_ 
import socket
import struct
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8880)) #連接服
while True:
 # 發(fā)收消息
 cmd = input('請你輸入命令>>:').strip()
 if not cmd:continue
 phone.send(cmd.encode('utf-8')) #發(fā)送

 #先收報(bào)頭
 header_struct = phone.recv(4) #收四個(gè)
 unpack_res = struct.unpack('i',header_struct)
 total_size = unpack_res[0] #總長度

 #后收數(shù)據(jù)
 recv_size = 0
 total_data=b''
 while recv_size<total_size: #循環(huán)的收
  recv_data = phone.recv(1024) #1024只是一個(gè)最大的限制
  recv_size+=len(recv_data) #
  total_data+=recv_data #
 print('返回的消息:%s'%total_data.decode('gbk'))
phone.close()

普通的服務(wù)端

# _*_ coding: utf-8 _*_ 
import socket
import subprocess
import struct
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機(jī)
phone.bind(('127.0.0.1',8880)) #綁定手機(jī)卡
phone.listen(5) #阻塞的最大數(shù)
print('start runing.....')
while True: #鏈接循環(huán)
 coon,addr = phone.accept()# 等待接電話
 print(coon,addr)
 while True: #通信循環(huán)

  # 收發(fā)消息
  cmd = coon.recv(1024) #接收的最大數(shù)
  print('接收的是:%s'%cmd.decode('utf-8'))

  #處理過程

  res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
           stdout=subprocess.PIPE, #標(biāo)準(zhǔn)輸出
           stderr=subprocess.PIPE #標(biāo)準(zhǔn)錯(cuò)誤
        )
  stdout = res.stdout.read()
  stderr = res.stderr.read()

  #先發(fā)報(bào)頭(轉(zhuǎn)成固定長度的bytes類型,那么怎么轉(zhuǎn)呢?就用到了struct模塊)
  #len(stdout) + len(stderr)#統(tǒng)計(jì)數(shù)據(jù)的長度
  header = struct.pack('i',len(stdout)+len(stderr))#制作報(bào)頭
  coon.send(header)

  #再發(fā)命令的結(jié)果
  coon.send(stdout)
  coon.send(stderr)
 coon.close()
phone.close()

5-3 優(yōu)化版的解決方法(從根本解決問題)

優(yōu)化的解決粘包問題的思路就是服務(wù)端將報(bào)頭信息進(jìn)行優(yōu)化,對要發(fā)送的內(nèi)容用字典進(jìn)行描述,首先字典不能直接進(jìn)行網(wǎng)絡(luò)傳輸,需要進(jìn)行序列化轉(zhuǎn)成json格式化字符串,然后轉(zhuǎn)成bytes格式服務(wù)端進(jìn)行發(fā)送,因?yàn)閎ytes格式的json字符串長度不是固定的,所以要用struct模塊將bytes格式的json字符串長度壓縮成固定長度,發(fā)送給客戶端,客戶端進(jìn)行接受,反解就會(huì)得到完整的數(shù)據(jù)包。

終極版的客戶端

# _*_ coding: utf-8 _*_ 
import socket
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) #連接服務(wù)器
while True:
 # 發(fā)收消息
 cmd = input('請你輸入命令>>:').strip()
 if not cmd:continue
 phone.send(cmd.encode('utf-8')) #發(fā)送

 #先收報(bào)頭的長度
 header_len = struct.unpack('i',phone.recv(4))[0] #吧bytes類型的反解

 #在收報(bào)頭
 header_bytes = phone.recv(header_len) #收過來的也是bytes類型
 header_json = header_bytes.decode('utf-8') #拿到j(luò)son格式的字典
 header_dic = json.loads(header_json) #反序列化拿到字典了
 total_size = header_dic['total_size'] #就拿到數(shù)據(jù)的總長度了

 #最后收數(shù)據(jù)
 recv_size = 0
 total_data=b''
 while recv_size<total_size: #循環(huán)的收
  recv_data = phone.recv(1024) #1024只是一個(gè)最大的限制
  recv_size+=len(recv_data) #有可能接收的不是1024個(gè)字節(jié),或許比1024多呢,
  # 那么接收的時(shí)候就接收不全,所以還要加上接收的那個(gè)長度
  total_data+=recv_data #最終的結(jié)果
 print('返回的消息:%s'%total_data.decode('gbk'))
phone.close()

終極版的服務(wù)端

# _*_ coding: utf-8 _*_ 
import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機(jī)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080)) #綁定手機(jī)卡
phone.listen(5) #阻塞的最大數(shù)
print('start runing.....')
while True: #鏈接循環(huán)
 coon,addr = phone.accept()# 等待接電話
 print(coon,addr)

 while True: #通信循環(huán)
  # 收發(fā)消息
  cmd = coon.recv(1024) #接收的最大數(shù)
  print('接收的是:%s'%cmd.decode('utf-8'))

  #處理過程
  res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
           stdout=subprocess.PIPE, #標(biāo)準(zhǔn)輸出
           stderr=subprocess.PIPE #標(biāo)準(zhǔn)錯(cuò)誤
        )
  stdout = res.stdout.read()
  stderr = res.stderr.read()

  # 制作報(bào)頭
  header_dic = {
   'total_size': len(stdout)+len(stderr), # 總共的大小
   'filename': None,
   'md5': None
  }
  header_json = json.dumps(header_dic) #字符串類型
  header_bytes = header_json.encode('utf-8') #轉(zhuǎn)成bytes類型(但是長度是可變的)

  #先發(fā)報(bào)頭的長度
  coon.send(struct.pack('i',len(header_bytes))) #發(fā)送固定長度的報(bào)頭

  #再發(fā)報(bào)頭
  coon.send(header_bytes)

  #最后發(fā)命令的結(jié)果
  coon.send(stdout)
  coon.send(stderr)
 coon.close()
phone.close()

六,struct模塊

了解c語言的人,一定會(huì)知道struct結(jié)構(gòu)體在c語言中的作用,它定義了一種結(jié)構(gòu),里面包含不同類型的數(shù)據(jù)(int,char,bool等等),方便對某一結(jié)構(gòu)對象進(jìn)行處理。而在網(wǎng)絡(luò)通信當(dāng)中,大多傳遞的數(shù)據(jù)是以二進(jìn)制流(binary data)存在的。當(dāng)傳遞字符串時(shí),不必?fù)?dān)心太多的問題,而當(dāng)傳遞諸如int、char之類的基本數(shù)據(jù)的時(shí)候,就需要有一種機(jī)制將某些特定的結(jié)構(gòu)體類型打包成二進(jìn)制流的字符串然后再網(wǎng)絡(luò)傳輸,而接收端也應(yīng)該可以通過某種機(jī)制進(jìn)行解包還原出原始的結(jié)構(gòu)體數(shù)據(jù)。python中的struct模塊就提供了這樣的機(jī)制,該模塊的主要作用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉(zhuǎn)化(This module performs conversions between Python values and C structs represented as Python strings.)。stuct模塊提供了很簡單的幾個(gè)函數(shù),下面寫幾個(gè)例子。

1,基本的pack和unpack

struct提供用format specifier方式對數(shù)據(jù)進(jìn)行打包和解包(Packing and Unpacking)。例如:

#該模塊可以把一個(gè)類型,如數(shù)字,轉(zhuǎn)成固定長度的bytes類型
import struct
# res = struct.pack('i',12345)
# print(res,len(res),type(res)) #長度是4
res2 = struct.pack('i',12345111)
print(res2,len(res2),type(res2)) #長度也是4
unpack_res =struct.unpack('i',res2)
print(unpack_res) #(12345111,)
# print(unpack_res[0]) #12345111

代碼中,首先定義了一個(gè)元組數(shù)據(jù),包含int、string、float三種數(shù)據(jù)類型,然后定義了struct對象,并制定了format‘I3sf',I 表示int,3s表示三個(gè)字符長度的字符串,f 表示 float。最后通過struct的pack和unpack進(jìn)行打包和解包。通過輸出結(jié)果可以發(fā)現(xiàn),value被pack之后,轉(zhuǎn)化為了一段二進(jìn)制字節(jié)串,而unpack可以把該字節(jié)串再轉(zhuǎn)換回一個(gè)元組,但是值得注意的是對于float的精度發(fā)生了改變,這是由一些比如操作系統(tǒng)等客觀因素所決定的。打包之后的數(shù)據(jù)所占用的字節(jié)數(shù)與C語言中的struct十分相似。

2,定義format可以參照官方api提供的對照表:

3,基本用法

import json,struct
#假設(shè)通過客戶端上傳1T:1073741824000的文件a.txt
#為避免粘包,必須自定制報(bào)頭
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T數(shù)據(jù),文件路徑和md5值

#為了該報(bào)頭能傳送,需要序列化并且轉(zhuǎn)為bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并轉(zhuǎn)成bytes,用于傳輸

#為了讓客戶端知道報(bào)頭的長度,用struck將報(bào)頭長度這個(gè)數(shù)字轉(zhuǎn)成固定長度:4個(gè)字節(jié)
head_len_bytes=struct.pack('i',len(head_bytes)) #這4個(gè)字節(jié)里只包含了一個(gè)數(shù)字,該數(shù)字是報(bào)頭的長度

#客戶端開始發(fā)送
conn.send(head_len_bytes) #先發(fā)報(bào)頭的長度,4個(gè)bytes
conn.send(head_bytes) #再發(fā)報(bào)頭的字節(jié)格式
conn.sendall(文件內(nèi)容) #然后發(fā)真實(shí)內(nèi)容的字節(jié)格式

#服務(wù)端開始接收
head_len_bytes=s.recv(4) #先收報(bào)頭4個(gè)bytes,得到報(bào)頭長度的字節(jié)格式
x=struct.unpack('i',head_len_bytes)[0] #提取報(bào)頭的長度
head_bytes=s.recv(x) #按照報(bào)頭長度x,收取報(bào)頭的bytes格式
header=json.loads(json.dumps(header)) #提取報(bào)頭

#最后根據(jù)報(bào)頭的內(nèi)容提取真實(shí)的數(shù)據(jù),比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python過濾序列元素的方法

    Python過濾序列元素的方法

    這篇文章主要介紹了Python過濾序列元素的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • python 利用Pyinstaller打包Web項(xiàng)目

    python 利用Pyinstaller打包Web項(xiàng)目

    這篇文章主要介紹了python 利用Pyinstaller打包Web項(xiàng)目,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2020-10-10
  • AI人工智能 Python實(shí)現(xiàn)人機(jī)對話

    AI人工智能 Python實(shí)現(xiàn)人機(jī)對話

    這篇文章主要為大家詳細(xì)介紹了AI人工智能應(yīng)用,本文擬使用Python開發(fā)語言實(shí)現(xiàn)類似于WIndows平臺(tái)的“小娜”,,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 基于Python構(gòu)建一個(gè)智能語音機(jī)器人

    基于Python構(gòu)建一個(gè)智能語音機(jī)器人

    這篇文章主要為大家詳細(xì)介紹了如何基于Python構(gòu)建一個(gè)智能語音機(jī)器人,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • PyQt5中QButtonGroup的用法解析與案例分享

    PyQt5中QButtonGroup的用法解析與案例分享

    在PyQt5中,QButtonGroup是一個(gè)非常有用的類,它提供了一個(gè)抽象的按鈕容器,允許開發(fā)者將多個(gè)按鈕劃分為一個(gè)組,本教程將詳細(xì)介紹QButtonGroup的創(chuàng)建、使用、信號(hào)槽連接以及在實(shí)際項(xiàng)目中的應(yīng)用案例,需要的朋友可以參考下
    2024-08-08
  • Python中動(dòng)態(tài)獲取對象的屬性和方法的教程

    Python中動(dòng)態(tài)獲取對象的屬性和方法的教程

    本文主要介紹了如何在Python中動(dòng)態(tài)獲取對象的屬性和方法,并運(yùn)行使用它們,需要的朋友可以參考一下
    2015-04-04
  • Python?print函數(shù):如何將對象打印輸出

    Python?print函數(shù):如何將對象打印輸出

    這篇文章主要介紹了Python?print函數(shù):如何將對象打印輸出,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • Python內(nèi)置模塊turtle繪圖詳解

    Python內(nèi)置模塊turtle繪圖詳解

    這篇文章主要介紹了Python內(nèi)置模塊turtle繪圖詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • python獲取指定網(wǎng)頁上所有超鏈接的方法

    python獲取指定網(wǎng)頁上所有超鏈接的方法

    這篇文章主要介紹了python獲取指定網(wǎng)頁上所有超鏈接的方法,涉及Python使用urllib2模塊操作網(wǎng)頁抓取的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • python使用turtle庫繪制奧運(yùn)五環(huán)

    python使用turtle庫繪制奧運(yùn)五環(huán)

    turtle也叫海龜,是turtle繪圖體系的python實(shí)現(xiàn),這篇文章主要介紹了python使用turtle庫繪制奧運(yùn)五環(huán),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2020-02-02

最新評(píng)論