Python期末编程总结(Socket编程实现鸢尾花类型分类查询以及天气预报功能)

Python期末总结

在这里插入图片描述

设计一个C/S结构的应用程序
根据所学内容,设计一个C/S结构的应用程序。要求如下:
1)可以采用Socket或者Tcp进行构建;
2)编程语言可以采用C#或者Python;
3)服务器端应采用多线程;
4)可能的情况下在服务器端进行异步处理;
5)尽量采用模块化编程;
6)服务器端应提供一种或多种服务,如鸢尾花识别,天气预报,房价预测等功能。客服端输入需要查询的数据信息,服务端对该信息提供查询判断结果,并将结果返回客户端。


撰写一份说明书
根据以上设计,撰写一份说明书(doc文档)。
说明书包括两个部分内容:操作说明和开发说明。
1)操作说明:即程序应如何使用,可以用图表加以说明。
2)开发说明:即各个模块采用了什么技术,按模块介绍代码的意义等。


评分标准(按以下组成部分给分)

程序可运行,具备基本功能(40分)
能综合使用相关技术,功能有所创新(30分)
说明书介绍思路清晰,模块完整(30分)

在这里插入图片描述

操作说明

鸢尾花数据集查询和天气预报
大概步骤是这样的

在这里插入图片描述

操作如下:

(1)启动main函数,此时服务端处于监听状态
在这里插入图片描述
(2)启动客户端
在这里插入图片描述
(3)输入正确数据查询返回鸢尾花类型,没找到会提示重新输入
在这里插入图片描述
(4)输入某某市以查询未来五日天气
在这里插入图片描述

开发说明

开发工具:PyCharm、Postman
所用技术:
Socket网络编程
多线程并发编程
Python常用语法
Socket中json字符串的传输
鸢尾花数据集分类算法
天气API调用
在这里插入图片描述

各模块代码

Main.py

# -*- coding: utf-8 -*-
"""
所有的方法已经通过异常捕捉,并限制了异常信息打印方式,防止不友好的信息抛出

@author: luopeng
"""


# 封装一系列操作到该函数中,下面直接调用即可
def main():
    # 导入多线程的包和我写好的套接字封装类
    import threading
    from com.lp.testiris.socket import TestSocketArgparseServer as sss
    # 调用parse_command_line()
    address = sss.parse_command_line()
    # 调用creat_srv_socket()方法获取listener对象
    listener = sss.creat_srv_socket(address)
    # while True:
    # 实现多线程监听accept_connections_forever
    for i in range(5):
        t = threading.Thread(target=sss.accept_connections_forever, args=(listener,))
        t.start()


#  调用定义好的main()开启服务端
# 服务端程序入口
if __name__ == '__main__':
    # while True:
    main()


CommentClient.py

# -*- coding: utf-8 -*-
"""
我将三个客户端的代码封装到了这儿,方便直接调用
@author: luopeng
"""
import json
from socket import *

Host = 'localhost'
Port = 1060
bufsize = 4096
Addr = (Host, Port)


def start():
    try:
        tcpCliSock = socket(AF_INET, SOCK_STREAM)
        tcpCliSock.connect(Addr)
        while True:
            print('声明:如果你想查询鸢尾花类型,请输入(a,b,c,d)的格式,如果你想查询城市未来五日天气,请输入:xx市')
            data = input('请输入(直接回车断开连接):')
            # 如果输入为空,直接断开,服务端我已经写好代码断开socket连接
            if not data:
                print("与服务器断开连接!")
                break
            # 将数据转换成字节发送至socket,服务端通过socket调用方法得到该数据
            tcpCliSock.sendall(bytes(data, 'utf-8'))
            # 通过recv()方法来接收服务端传来的数据
            data = tcpCliSock.recv(bufsize)
            # 将data转换为utf8编码
            data = data.decode('utf-8')
            if not data.endswith('C'):
                print(data)
            else:
                data = data.replace('C', '')
                # 如果接收到的是错误提示则输出提示即可
                if data.endswith('!'):
                    print(data)
                else:
                    data = json.loads(data)
                    print("城市:", data["data"]["city"])
                    print("当前温度:", data["data"]["wendu"])
                    print("提醒:", data["data"]["ganmao"])
                    for i in range(5):
                        print("时间:", data["data"]["forecast"][i]["date"])
                        print("温度:", data["data"]["forecast"][i]["high"], data["data"]["forecast"][i]["low"])
                        print("天气:", data["data"]["forecast"][i]["type"])
                        print("------------------------------------------------------------------------------")

    except Exception as e:
        print(e, "连接中断,请尝试重新连接")
        tcpCliSock.close()

TestSocketArgparseServer.py

# -*- coding: utf-8 -*-
"""
@author: luopeng
"""

import argparse, socket
from com.lp.testiris.data.Moder import pred_iris
from com.lp.testiris.data import WeatherAPI as wt


# 命令行获取ip和端口
def parse_command_line():
    """解析命令行并返回一个套接字地址"""
    parser = argparse.ArgumentParser(description='')
    # 该处我给ip和端口设置了默认值,方便在开发工具中测试,方便数据的观察和调试
    parser.add_argument('-host', help=' IP or hostname', default='127.0.0.1')
    parser.add_argument('-p', metavar='port', type=int, default=1060,
                        help='TCP port (default 1060)')
    args = parser.parse_args()
    address = (args.host, args.p)
    return address


# 返回listener,主函数中调用该方法可供accept_connections_forever()传参
def creat_srv_socket(address):
    """Build and return a listen server socket."""
    listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listener.bind(address)
    listener.listen(5)
    print('Listening at{}'.format(address))
    return listener


# 监听
def accept_connections_forever(listener):
    """总是回应传入连接的监听套接字"""
    while True:
        sock, address = listener.accept()
        print('已经连接上:', address)
        handle_conversation(sock, address)


"""通过套接字与客户端交互,直到中断连接"""


def handle_conversation(sock, address):
    try:
        while True:
            handle_request(sock)
    except EOFError:
        print('与{}的连接已断开!'.format(address))
    except Exception as e:
        print('client {} 已断开连接!'.format(address))
    finally:
        sock.close()


def handle_request(sock):
    """在sock上接收到一个客户端请求并给予回应"""
    # 尝试获取数据
    aphorisms = recv_until(sock)
    # 如果没有数据则return
    # handle_conversation()方法中捕捉异常状态,断开该连接
    if not aphorisms:
        # 如果raise抛出异常, 程序后面的代码不会再执行.
        raise Exception('有客户端已断开连接')

    # 接收到数据转换成utf8编码格式
    x = aphorisms.decode('utf_8')
    if x.endswith('市'):
        # 将客户端传过来的值通过getWeather()方法来查询是否有该值
        returnData = wt.get_weather(x) + 'C'
        # 返回接收到的数据到客户端
        sock.sendall(bytes(returnData, 'utf_8'))
        # 控制台打印一下对象
        print("返回未来五日天气:", returnData)
    else:
        # 将客户端传过来的值通过pred_iris()方法来查询是否有该值
        returnData = pred_iris(x)
        # 返回接收到的数据到客户端
        sock.sendall(bytes(returnData, 'utf_8'))
        print("返回鸢尾花类型:", returnData)


# 接收客户端数据,该方法只负责数据的接收,验证工作交给专门的方法来执行
def recv_until(sock):
    while True:
        message = sock.recv(4096)
        return message

Moder.py

# -*- coding: utf-8 -*-
"""
@author: luopeng
"""

import numpy as np
from sklearn.neighbors import KNeighborsClassifier as KNN

sourceLabel = ['setosa','versicolor','virginica']

def loadDataSet(fileName):
    try:
        dataMat = []
        lbl=[]

        with open(fileName) as fr:
            for line in fr:
                curLine=line.strip().split(',')

                fltLine = [np.float(curLine[i]) for i in range(len(curLine)-1)]
                dataMat.append(fltLine)
                lbl.append(sourceLabel.index(curLine[-1]))
        return np.array(dataMat),np.array(lbl)
    except Exception as e:
        print(e)
def concertStrToInt(data):
    strX=data.strip().split(',')
    floatX=[float(s) for s in strX]
    return[floatX]
    
def pred_iris(x):
    X,y=loadDataSet('iris.txt')
    knn=KNN(n_neighbors=5)
    knn.fit(X,y)
    try:
        x=concertStrToInt(x)
        y_pred=knn.predict(x)
    except Exception as e:
        return "没有找到数据,请重新输入!"

    return sourceLabel[y_pred[0]]

WeatherAPI.py

import requests, json

weatherUrl = "http://wthrcdn.etouch.cn/weather_mini?city="  # 返回json数据


# cityName = input("请输入你要查询的城市:")

def get_weather(city_name):
    # 组合得到当前城市天气API
    weatherResp = requests.get(weatherUrl + city_name)
    data = weatherResp.json()
    return get_data(data)


# 返回天气数据对象
def get_data(data):
    try:
        if data['status'] == 1000:
            return json.dumps(data)
            # print("城市:", data["data"]["city"])
            # print("当前温度:", data["data"]["wendu"])
            # print("提醒:", data["data"]["ganmao"])
            # for i in range(5):
            #     print("时间:", data["data"]["forecast"][i]["date"])
            #     print("温度:", data["data"]["forecast"][i]["high"], data["data"]["forecast"][i]["low"])
            #     print("天气:", data["data"]["forecast"][i]["type"])
            #     print("------------------------------------------------------------------------------")
        else:
            return "请输入正确的城市名称!"
    except Exception as e:
        # print("请输入正确的城市!")
        print(e)
        return "请输入正确的城市名称!"

Client1.py

# -*- coding: utf-8 -*-
"""
@author: luopeng
"""
from com.lp.testiris.common import CommentClient as Client

Client.start()

iris.txt

5.1,3.5,1.4,0.2,setosa
4.9,3,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.4,3.7,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
4.8,3,1.4,0.1,setosa
4.3,3,1.1,0.1,setosa
5.8,4,1.2,0.2,setosa
5.7,4.4,1.5,0.4,setosa
5.4,3.9,1.3,0.4,setosa
5.1,3.5,1.4,0.3,setosa
5.7,3.8,1.7,0.3,setosa
5.1,3.8,1.5,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.1,3.7,1.5,0.4,setosa
4.6,3.6,1,0.2,setosa
5.1,3.3,1.7,0.5,setosa
4.8,3.4,1.9,0.2,setosa
5,3,1.6,0.2,setosa
5,3.4,1.6,0.4,setosa
5.2,3.5,1.5,0.2,setosa
5.2,3.4,1.4,0.2,setosa
4.7,3.2,1.6,0.2,setosa
4.8,3.1,1.6,0.2,setosa
5.4,3.4,1.5,0.4,setosa
5.2,4.1,1.5,0.1,setosa
5.5,4.2,1.4,0.2,setosa
4.9,3.1,1.5,0.2,setosa
5,3.2,1.2,0.2,setosa
5.5,3.5,1.3,0.2,setosa
4.9,3.6,1.4,0.1,setosa
4.4,3,1.3,0.2,setosa
5.1,3.4,1.5,0.2,setosa
5,3.5,1.3,0.3,setosa
4.5,2.3,1.3,0.3,setosa
4.4,3.2,1.3,0.2,setosa
5,3.5,1.6,0.6,setosa
5.1,3.8,1.9,0.4,setosa
4.8,3,1.4,0.3,setosa
5.1,3.8,1.6,0.2,setosa
4.6,3.2,1.4,0.2,setosa
5.3,3.7,1.5,0.2,setosa
5,3.3,1.4,0.2,setosa
7,3.2,4.7,1.4,versicolor
6.4,3.2,4.5,1.5,versicolor
6.9,3.1,4.9,1.5,versicolor
5.5,2.3,4,1.3,versicolor
6.5,2.8,4.6,1.5,versicolor
5.7,2.8,4.5,1.3,versicolor
6.3,3.3,4.7,1.6,versicolor
4.9,2.4,3.3,1,versicolor
6.6,2.9,4.6,1.3,versicolor
5.2,2.7,3.9,1.4,versicolor
5,2,3.5,1,versicolor
5.9,3,4.2,1.5,versicolor
6,2.2,4,1,versicolor
6.1,2.9,4.7,1.4,versicolor
5.6,2.9,3.6,1.3,versicolor
6.7,3.1,4.4,1.4,versicolor
5.6,3,4.5,1.5,versicolor
5.8,2.7,4.1,1,versicolor
6.2,2.2,4.5,1.5,versicolor
5.6,2.5,3.9,1.1,versicolor
5.9,3.2,4.8,1.8,versicolor
6.1,2.8,4,1.3,versicolor
6.3,2.5,4.9,1.5,versicolor
6.1,2.8,4.7,1.2,versicolor
6.4,2.9,4.3,1.3,versicolor
6.6,3,4.4,1.4,versicolor
6.8,2.8,4.8,1.4,versicolor
6.7,3,5,1.7,versicolor
6,2.9,4.5,1.5,versicolor
5.7,2.6,3.5,1,versicolor
5.5,2.4,3.8,1.1,versicolor
5.5,2.4,3.7,1,versicolor
5.8,2.7,3.9,1.2,versicolor
6,2.7,5.1,1.6,versicolor
5.4,3,4.5,1.5,versicolor
6,3.4,4.5,1.6,versicolor
6.7,3.1,4.7,1.5,versicolor
6.3,2.3,4.4,1.3,versicolor
5.6,3,4.1,1.3,versicolor
5.5,2.5,4,1.3,versicolor
5.5,2.6,4.4,1.2,versicolor
6.1,3,4.6,1.4,versicolor
5.8,2.6,4,1.2,versicolor
5,2.3,3.3,1,versicolor
5.6,2.7,4.2,1.3,versicolor
5.7,3,4.2,1.2,versicolor
5.7,2.9,4.2,1.3,versicolor
6.2,2.9,4.3,1.3,versicolor
5.1,2.5,3,1.1,versicolor
5.7,2.8,4.1,1.3,versicolor
6.3,3.3,6,2.5,virginica
5.8,2.7,5.1,1.9,virginica
7.1,3,5.9,2.1,virginica
6.3,2.9,5.6,1.8,virginica
6.5,3,5.8,2.2,virginica
7.6,3,6.6,2.1,virginica
4.9,2.5,4.5,1.7,virginica
7.3,2.9,6.3,1.8,virginica
6.7,2.5,5.8,1.8,virginica
7.2,3.6,6.1,2.5,virginica
6.5,3.2,5.1,2,virginica
6.4,2.7,5.3,1.9,virginica
6.8,3,5.5,2.1,virginica
5.7,2.5,5,2,virginica
5.8,2.8,5.1,2.4,virginica
6.4,3.2,5.3,2.3,virginica
6.5,3,5.5,1.8,virginica
7.7,3.8,6.7,2.2,virginica
7.7,2.6,6.9,2.3,virginica
6,2.2,5,1.5,virginica
6.9,3.2,5.7,2.3,virginica
5.6,2.8,4.9,2,virginica
7.7,2.8,6.7,2,virginica
6.3,2.7,4.9,1.8,virginica
6.7,3.3,5.7,2.1,virginica
7.2,3.2,6,1.8,virginica
6.2,2.8,4.8,1.8,virginica
6.1,3,4.9,1.8,virginica
6.4,2.8,5.6,2.1,virginica
7.2,3,5.8,1.6,virginica
7.4,2.8,6.1,1.9,virginica
7.9,3.8,6.4,2,virginica
6.4,2.8,5.6,2.2,virginica
6.3,2.8,5.1,1.5,virginica
6.1,2.6,5.6,1.4,virginica
7.7,3,6.1,2.3,virginica
6.3,3.4,5.6,2.4,virginica
6.4,3.1,5.5,1.8,virginica
6,3,4.8,1.8,virginica
6.9,3.1,5.4,2.1,virginica
6.7,3.1,5.6,2.4,virginica
6.9,3.1,5.1,2.3,virginica
5.8,2.7,5.1,1.9,virginica
6.8,3.2,5.9,2.3,virginica
6.7,3.3,5.7,2.5,virginica
6.7,3,5.2,2.3,virginica
6.3,2.5,5,1.9,virginica
6.5,3,5.2,2,virginica
6.2,3.4,5.4,2.3,virginica
5.9,3,5.1,1.8,virginica

1、先来分析一下在开发过程中遇见的错误以及解决方案

(1)在最开始,程序会因为遇见异常而终止,或者因异常而使客户端直接报错断开,我通过try–except代码块来捕获异常并有效的处理了异常显示在客户端的尴尬境遇!
(2)调用天气API时遇见了很多坑,最大的坑就是属于在客户端发送数据过来时不能返回有效的数据,甚至多次玩崩了程序
我通过Postman谷歌开发工具来测试了一下数据的可接收性,通过断点调试,最终发现数据确实接收到的,只是不能发送到客户端,我去了解了一下JSON数据在Socket中怎么传输,后来查到,对于JSON字符串是需要先将其通过json.dumps()方法来编码才能传输,后来传输测试通过了,就该想怎样设计才能让客户端发送过来的消息得到区别,我加上了if判断,如果客户端发送过来的字符串是以‘市’结尾,则让其去调用天气的API,并响应结果,否则就直接调用鸢尾花数据集算法,该类中由于我设置好了标准的开发包结构,最后发现一直无法找到txt文件夹,我多次尝试,发现还是不对,最后网上找到解决方案:其实Python中相对路径下寻找文件,是相对main函数的所在包,并不是相对当前所在包!在这里插入图片描述
(3)完成了数据的传输,又来了一点问题,我传输鸢尾花类型是简单的str,但是我传输过去的天气JSON是通过json.dumps()编码后的二进制字符,在客户端必须对两种数据再进行判断,鸢尾花数据不需要解码,天气JSON必须要解码才能够,那我以什么作为判断条件呢?因为该程序只有两种可能,所以我直接给天气JSON字符串后再返回时加了一个C,以方便我在客户端昨天见判断,就是因为这个C,我花了两个小时修复它的bug!在这里插入图片描述
(4)说上面的C的bug,由于我返回的JSON数据结尾有一个C,我在客户端只顾着判断,却忘记了将它去掉,最后导致我的程序一直出现错误,我多次debug,却还是没发现。到后面才恍然大悟,才发现的那个大大的C,于是我用类型判断一下当前接收到的data是什么类型,判断出来时str,于是开始调用str的API,将字符串replace方法来将C用空字符来代替,简单的程序,我调用的天气API接口返回中没有该大写C,所以选择直接替换,就没有用字符串切割split方法!
(5)在上一步说到字符解码,但是我传回来还可能是我预设的错误信息return "请输入正确的城市名称!",所以我在字符代替后面再加上了条件判断,如果返回的是以!结尾的,则直接打印即可!

2、分析一下各个模块的功能

对于每一个模块的代码我都做好了很细致的注释,这里通过开发的流程来一步步解释!
(1)Main.py模块
该模块是程序的起点,是服务端程序的入口,在该模块中实现多线程监听accept_connections_forever方法,通过当前模块来调用了TestSocketArgparseServer.py模块
(2)TestSocketArgparseServer.py
Socket的全部核心操作几乎都是写在这一个模块里面的
以下是我对所有模块的交互图解
在这里插入图片描述

总结

该程序设计并没有难点,只是糅合了非常多的小型知识点,以及一些模块需要我们自己的扩展知识,对于该程序,在天气API调用中,我是把显示工作交给客户端完成的,这很符合实际开发,我在开发Java的B/S架构程序中,后端不需要任何的界面化操作,只需要传递一个json对象即可,所有的调用都是前端的工作,这也是现在潮流,因为前后端分离必定有利于开发工作,不再像以前jsp时代的前后端冗余操作,开发效率极低,我在博客中也有记录很多开发相关事宜以及学习记录,也去通过Vue框架调用过天气API和网易云音乐的API,做了个小型播放器,所有语言的调用方式都是一样的,只是代码的规范不同,先前一直以为前端调用天气API很简单,现在做了一下Python,发现其实都简单,只是简单地通过调用后所得到的json进行对象调用,Python作为一门脚本语言来说其用处远远不是这些操作,Python是人工智能的主导人工智能开发,Java是做后端开发,所有语言都有其独特的领域,Java跨平台是亮点,一套Spring全家桶引领了一个时代,Python则是突出且完美融合于人工智能,算法等一系列开发,这一学期通过多种程序之间的设计,也领悟了很多,不光是以前只会调用简单的程序API那么简单了,更重要的是逻辑能力以及业务理解能力,这次做的程序虽然最终是解决了所有操作的实现,但是我会很容易的发现但凡我后面需要增加模块,用到循环操作,我就会出现很大的漏洞,因为我写循环语句会很容易的就使时间复杂度变成了O(n),感觉自己很多时候想要优化又会局限于算法的掌握不好,过段时间微服务学习完成也会去好好刷一刷LeetCode,积累点算法优化知识,这既是对编程课的总结,同时也是对一学期所学的总结,原创声明,个人博客个人博客   CSDN博客!总结完毕!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值