Python笔记(五)

1. HTTP协议

1.1 什么是url

URL又称统一资源定位符,是资源在网络上存放的地址。由协议+域名+路径组成。例如:http://www.shanshimantang.com/index.html。其中http是协议,www.shanshimantang.com是域名,index.html就是路径。

1.2 浏览器访问Web服务器的过程

  1. 首先通过DNS将域名解析成对应的IP地址。
  2. 然后与IP地址建立连接
  3. 发送HTTP请求数据
  4. 服务器程序根据请求去获取资源数据
  5. 然后将获取到资源返给Web服务器。

1.3 请求方式和状态码

http请求方式分为get请求和post请求
每个请求都会返回状态码,常见的状态码有以下几种:

状态码描述
200请求成功
400地址参数有误
404请求资源不存在
500服务器内部源代码错误

2. 静态服务器

2.1 Python自带的静态服务器

在想要启动服务的目录下,打开cmd里面执行命令:python3 -m http.server 端口号 可以启动python自带的静态服务器。
在这里插入图片描述
在这里插入图片描述

2.2 tcp实现服务器返回

使用socket实现浏览器访问的返回,分为以下几步:

  1. 创建socket服务器对象
  2. 绑定ip和端口号
  3. 设置端口复用
  4. 设置监听
  5. 等待客户端连接
  6. 接收客户端发送的消息
  7. 给客户端发送消息
  8. 关闭连接
import socket

if __name__ == '__main__':
    # 创建socket对象
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 绑定ip和端口
    tcp_server.bind(("", 6699))
    # 设置端口复用
    tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 设置监听
    tcp_server.listen(128)
    # 等待客户端连接
    print("正在等待客户端连接")
    while True:
        conn, addr = tcp_server.accept()
        print("已经连接到:", addr)
        # 接收消息
        recv_data = conn.recv(124).decode()
        if not recv_data:
            print("没有收到消息")
            break
        print(f"收到的消息内容:{recv_data}")
        # 发送消息
        resp_data = conn.send("".encode())
        # 关闭连接
        conn.close()
    tcp_server.close()


按照以上代码的设计,客户端访问后不会显示任何东西,原因是:没有遵从http协议去返回消息。因此还需要构建符合协议规则的响应消息去返回。

# 发送消息
        with open("./static/index.html", "rb") as f:
            file_data = f.read()
        # 构造响应码
        resp_line = "HTTP/1.1 200 OK\r\n"
        # 构造响应头
        resp_header = "Content-Type: text/html\r\n\r\n"
        # 构造响应体
        resp_body = file_data
        # 组建响应消息
        resp_data = (resp_line + resp_header).encode() + resp_body
        conn.send(resp_data)

至此,就实现了静态服务器的基本功能了,但是目前还有一个缺陷就是,只能返回固定数据,无论我是去访问/index.html还是访问/123.action,返回的都是固定的数据。所以下面还需要对请求内容进行区分,访问不存在的资源时,返回响应的404界面。
实现思路:通过执行的打印内容来看,收到的消息,里面第二个就是请求的内容,因此可以将请求的内容串分割,然后取指定的数据来进行判断区分。

正在等待客户端连接
已经连接到: ('127.0.0.1', 59863)
收到的消息内容:GET /index.html HTTP/1.1
Host: 127.0.0.1:6699
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not_A Brand";v
已经连接到: ('127.0.0.1', 59864)
收到的消息内容:GET /index.html HTTP/1.1
Host: 127.0.0.1:6699
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not_A Brand";v
已经连接到: ('127.0.0.1', 59865)
没有收到消息

进程已结束,退出代码为 0

代码实现:

import socket

if __name__ == '__main__':
    # 创建socket对象
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 绑定ip和端口
    tcp_server.bind(("", 6699))
    # 设置端口复用
    tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 设置监听
    tcp_server.listen(128)
    # 等待客户端连接
    print("正在等待客户端连接")
    while True:
        conn, addr = tcp_server.accept()
        print("已经连接到:", addr)
        # 接收消息
        recv_data = conn.recv(124).decode()
        if not recv_data:
            print("没有收到消息")
            break
        print(f"收到的消息内容:{recv_data}")
        # 增加判断请求内容
        recv_data_list = recv_data.split(" ")
        print(f"请求内容是{recv_data_list[1]}")
        if recv_data_list[1] == "" or recv_data_list[1] == "/index.html":
            with open("./static/index.html", "rb") as f:
                file_data = f.read()
        else:
            with open("./static/404.html", "rb") as f:
                file_data = f.read()
        # 构造响应码
        resp_line = "HTTP/1.1 200 OK\r\n"
        # 构造响应头
        resp_header = "Content-Type: text/html\r\n\r\n"
        # 构造响应体
        resp_body = file_data
        resp_data = (resp_line + resp_header).encode() + resp_body
        # 发送消息
        conn.send(resp_data)
        # 关闭连接
        conn.close()
    tcp_server.close()


2.3 多任务处理

多任务可以实现多个客户端的请求。

import socket
import threading


def tcp_server_handle(connection):
    # 接收消息
    recv_data = connection.recv(124).decode()
    if not recv_data:
        print("没有收到消息")
        return
    print(f"收到的消息内容:{recv_data}")
    # 增加判断请求内容
    recv_data_list = recv_data.split(" ")
    print(f"请求内容是{recv_data_list[1]}")
    """
    根据请求的内容,去对应的目录下面读取指定的文件,如果文件不存在就抛出404
    """
    req_file_path = recv_data_list[1]
    if req_file_path == "/":
        req_file_path = "/index.html"
    try:
        with open("./static" + req_file_path, "rb") as f:
            file_data = f.read()
    except FileNotFoundError:
        print("请求内容不存在")
        with open("./static/404.html", "rb") as f:
            file_data = f.read()
    # 构造响应码
    resp_line = "HTTP/1.1 200 OK\r\n"
    # 构造响应头
    resp_header = "Content-Type: text/html\r\n\r\n"
    # 构造响应体
    resp_body = file_data
    resp_data = (resp_line + resp_header).encode() + resp_body
    # 发送消息
    connection.send(resp_data)
    # 关闭连接
    connection.close()


if __name__ == '__main__':
    # 创建socket对象
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 绑定ip和端口
    tcp_server.bind(("", 6699))
    # 设置端口复用
    tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 设置监听
    tcp_server.listen(128)
    # 等待客户端连接
    print("正在等待客户端连接")
    while True:
        conn, addr = tcp_server.accept()
        print("已经连接到:", addr)
        # 创建线程,实现多任务处理
        child_thread = threading.Thread(target=tcp_server_handle, args=(conn,))
        child_thread.start()

    # tcp_server.close()

2.4 面向对象版

使用面向对象可以提高复用性,上面多任务版本中,端口号和ip是固定的,在实际应用中的使用性不强,因此需要对外提供自定义端口和ip。

import socket
import sys
import threading


class MyServer(object):
    def __init__(self, ip, port):
        self.IP = ip
        self.Port = port
        # 创建socket对象
        self.tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 绑定ip和端口
        self.tcp_server.bind((self.IP, self.Port))
        # 设置端口复用
        self.tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 设置监听
        self.tcp_server.listen(128)

    def __server_handle(self, connection):
        # 接收消息
        recv_data = connection.recv(124).decode()
        if not recv_data:
            print("没有收到消息")
            return
        print(f"收到的消息内容:{recv_data}")
        # 增加判断请求内容
        recv_data_list = recv_data.split(" ")
        req_file_path = recv_data_list[1]
        print(f"请求内容是{recv_data_list[1]}")
        """
        根据请求的内容,去对应的目录下面读取指定的文件,如果文件不存在就抛出404
        """
        if req_file_path == "/":
            req_file_path = "/index.html"
        try:
            with open("./static" + req_file_path, "rb") as f:
                file_data = f.read()
        except FileNotFoundError:
            print("请求内容不存在")
            with open("./static/404.html", "rb") as f:
                file_data = f.read()
        # 构造响应码
        resp_line = "HTTP/1.1 200 OK\r\n"
        # 构造响应头
        resp_header = "Content-Type: text/html\r\n\r\n"
        # 构造响应体
        resp_body = file_data
        resp_data = (resp_line + resp_header).encode() + resp_body
        # 发送消息
        connection.send(resp_data)
        # 关闭连接
        connection.close()

    def my_tcp_server(self):
        # 等待客户端连接
        print("正在等待客户端连接")
        while True:
            conn, addr = self.tcp_server.accept()
            print("已经连接到:", addr)
            # 创建线程,实现多任务处理
            child_thread = threading.Thread(target=self.__server_handle, args=(conn,))
            child_thread.start()


if __name__ == '__main__':
    # 一般都在Linux上使用命令行输入参数
    params_list = sys.argv	# 获取执行python程序的终端命令行参数
    # isdigit() 方法判断字符串是否是整型字符串
    if len(params_list) == 2 and not params_list[0].isdigit() and params_list[1].isdigit():
        static_server = MyServer(params_list[0], params_list[1])
        static_server.my_tcp_server()
    elif len(params_list) == 1:
        static_server = MyServer("127.0.0.1", 6699)
        static_server.my_tcp_server()
    else:
        print("启动命令为:python object_static_server.py <port>\t例如:python object_static_server.py 8080")

3. 闭包

3.1 函数参数

函数名存放的时函数所在空间的地址。
函数名也可以像普通变量一样赋值
执行函数名()就是执行函数名所存放空间地址中的代码。

3.2 闭包

3.2.1 闭包的简介

闭包可以保存函数内的变量,不会随着函数调用完而销毁。闭包的构成:

  1. 在函数嵌套(函数里面再定义函数)的前提下
  2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 外部函数返回了内部函数
    其中使用外部函数变量的内部函数称为闭包。调用闭包就相当于调用内部函数。

例如以下代码中,out_func函数中嵌套了inner_func函数,且inner_func函数中还用了out_func函数的参数num1,且out_func函数返回了inner_func,所以inner_func函数就是个闭包。

def out_func(num1):
    def inner_func(num2):
        print('{num1 + num2}')
    return inner_func

3.2.2 闭包的参数

使用nolocal可以修改闭包内的外部变量。以下代码中:

  1. 先执行out_func(“wang”),此时会执行函数out_func,且入参name = “wang”
  2. 遇到def 先不执行,继续向下执行print(f'{name}:{new_name}'),即输出wang:newname
  3. 然后执行return,即此时 wang = inner_func
  4. 然后执行 wang("欲买桂花同载酒"),即 执行inner_func(“欲买桂花同载酒”)
  5. 然后执行到new_name = "ceyyen"时,此时变量被修改,然后继续执行
  6. 输出wang:欲买桂花同载酒 和 wang:ceyyen
def out_func(name):
    new_name = "newname"
    info = "终不似,少年游"

    def inner_func(info):
        nonlocal new_name
        new_name = "ceyyen"
        print(f'{name}:{info}')
        print(f'{name}:{new_name}')

    print(f'{name}:{new_name}')
    return inner_func

wang = out_func("wang")
wang("欲买桂花同载酒")

4. 装饰器

4.1 什么是装饰器

装饰器就是把一个函数当做参数传递给闭包中的外部函数,同时在内部函数中使用这个函数,并给他添加新的功能。

def out_func(fn):
    def inner_func():
        print('Hello')
        fn()
    return inner_func

例如上面的代码中,把函数fn当做入参传给了外部函数out_func,同时在内部函数inner_func中使用了这个函数,同时inner_func函数还增加了print(‘Hello’) 的功能。

4.2 作用

装饰器的作用是:在不改变原有函数的源代码的情况下,给函数增加新的功能。
例如:有一个聊天系统,可以聊天,但是在使用聊天功能前,要先登录系统。
以上就可以使用装饰器来实现,下面是两种方法

4.2.1 常规代码

"""
def my_decorator(func):
    def inner():
        print("正在检查登录状态")
        func()

    return inner


# 例如:有一个聊天系统,可以聊天,但是在使用聊天功能前,要先登录系统。
def chatting():
    print("ceyyen:你好")


chat = my_decorator(chatting)
chat()

4.2.2 装饰器语法糖实现功能

def my_decorator(func):
    def inner():
        print("正在检查登录状态")
        func()

    return inner


# 例如:有一个聊天系统,可以聊天,但是在使用聊天功能前,要先登录系统。
@my_decorator
def talking():
    print("ceyyen:你好")


talking()

4.3 使用装饰器计算函数执行时间

def reckon_time(func):
    def inner():
    	# 定义开始时间
        start_time = time.time()
        func()
        # 定义结束时间
        end_time = time.time()
        print(f"执行时间是:{end_time - start_time}")

    return inner


@reckon_time
def total():
    num = 0
    for i in range(10):
        time.sleep(1)
        num += i
    print(num)

4.4 通用装饰器

通用装饰器有以下几个特征:

  1. 函数有参数,则装饰器中的内部函数和外部函数入参都要有参数,且参数要一致
  2. 函数有返回值,则在内部函数中要加返回值
  3. 如果参数为不定长参数,则装饰器的内部函数和外部函数也要改为不定长参数
def my_current_decorator(func):
    def inner(*args, **kwargs):
        print("新功能添加此处")
        return func(*args, **kwargs)

    return inner


@my_current_decorator
def playing(*args, **kwargs):
    print(args)
    print(kwargs)


playing("20240101", name="ceyyen")

4.5 多个装饰器

多个装饰器的装饰顺序是:就近原则,
多个装饰器的执行顺序是:就远原则
例如下面的例子:
先装饰current_time_decorator,再去装饰my_current_decorator
执行顺序则是:先执行my_current_decorator,再去执行current_time_decorator
即:playing("20240101", name="ceyyen") = my_current_decorator(current_time_decorator(playing))("20240101", name="ceyyen")

def current_time_decorator(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(f"执行时间是:{end_time - start_time}")

    return inner


def my_current_decorator(func):
    def inner(*args, **kwargs):
        print("新功能添加此处")
        return func(*args, **kwargs)

    return inner


@my_current_decorator
@current_time_decorator
def playing(*args, **kwargs):
    print(args)
    time.sleep(2)
    print(kwargs)


playing("20240101", name="ceyyen")

4.6 带参数的装饰器

我想使用装饰器实现传入运算符,识别两个数字的运算。
即在原有装饰器外层加一层,用来传递参数。

def number_crunching_decorator(flag):
    def out_func(func):
        def inner(*args, **kwargs):
            if flag == "+":
                print("加法运算")
            elif flag == "-":
                print("减法运算")
            return func(*args, **kwargs)

        return inner

    return out_func


@number_crunching_decorator("+")
def numbers(a, b):
    print(a + b)


numbers(10, 3)

4.7 类装饰器

4.7.1 __call__方法

一旦类中有__call__方法,那么这个类创建的对象就是一个可调用的对象,可以像调用函数一样进行调用。

4.7.2 案例

还是以聊天系统为例。
例如:有一个聊天系统,可以聊天,但是在使用聊天功能前,要先登录系统。

class CheckLogin(object):
    def __init__(self, fn):
        self.fn = fn

    def __call__(self, *args, **kwargs):
        print("正在检查登录状态")
        self.fn(*args, **kwargs)


@CheckLogin
def saying():   # saying = CheckLogin(saying)
    print("hello")


saying()

4.8 property属性

property属性就是负责把类中的每一个方法当做属性进行使用。
定义property属性有两种方式,分别是使用装饰器、类属性。

4.8.1 装饰器

@property表示把方法当做属性使用,表示获取属性时会执行下面修饰的方法。
@方法名.setter表示把方法当做属性使用,表示当设置属性时,会执行下面修饰的方法。
使用装饰器方式要保证以上两个装饰器修饰的方法名一定要一样。

class Person(object):
    def __init__(self, name):
        self.name = name
        self.__age = 18

    @property
    def get_age(self):
        return self.__age

    @get_age.setter
    def get_age(self, age):
        self.__age = age


stu = Person("ceyyen")
print(stu.get_age)
stu.get_age = 24
print(stu.get_age)

在这里插入图片描述

4.8.2 类属性方式

用法:age = property(获取方法,设置方法)

class Person(object):
    def __init__(self, name):
        self.name = name
        self.__age = 18
        self.__money = 10000

    def get_money(self):
        return self.__money

    def set_money(self, money):
        self.__money = money

    money = property(fget=get_money, fset=set_money)


stu = Person("ceyyen")
print(stu.get_age)
stu.get_age = 24
print(stu.get_age)
  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值