Python 基础教程

本贴最后更新于 566 天前,其中的信息可能已经沧海桑田

python 基础

HelloWorld

python 是 .py​ 文件,HelloWorld 的写法可以作

if __name__ == "__main__": # 入口程序 只有在运行程序的时候可以运行
	print("HelloWorld")

if __name__ == "__main__"​代码可以理解成程序的入口 只有程序在被 运行 的时候才会进入这里面的代码 而在被调用的时候是不会执行代码的相当于 Java 的 main 方法 print ​​是 python 内置的函数用于执行打印语句 # 是 python 的单行注释

注释的分类

这里的注释分类适用与任何的语言这里一同讲解

区块注释 - 折叠注释

import pymysql

#region 这里的代码用于干链接数据库的事情

conn=pymysql.connect(host = '127.0.0.1' # 连接名称,默认127.0.0.1
,user = 'root' # 用户名
,passwd='password' # 密码
,port= 3306 # 端口,默认为3306
,db='test' # 数据库名称
,charset='utf8' # 字符编码
)
cur = conn.cursor() # 生成游标对象
sql="select * from `student` " # SQL语句
cur.execute(sql) # 执行SQL语句

#endregion

用于将一个区域的代码进行容纳 并注释其代码的作用 比较优秀的解释器可以将区块注释进行折叠所以叫做折叠注释

TODO 注释 - 待办事项注释

import pymysql

conn=pymysql.connect(host = '127.0.0.1' # 连接名称,默认127.0.0.1
# TODO 需要填充mysql参数
)
cur = conn.cursor() # 生成游标对象
sql="select * from `student` " # SQL语句
cur.execute(sql) # 执行SQL语句

TODO 注释就是这里的代码需要实现某些功能但是没有实现 类似于待办事项

FIXME 注释

import pymysql

conn=pymysql.connect(host = '127.0.0.1' # 连接名称,默认127.0.0.1
,user = 'root' # 用户名
,passwd='password' # 密码
,port= 3306 # 端口,默认为3306
,db='test' # 数据库名称
,charset='utf8' # 字符编码
)
cur = conn.cursor() # 生成游标对象
sql="select * from `student` " # SQL语句

# FIXME 这里游标无法被读取
cu.execute(sql) # 执行SQL语句

可以拆成短语,fix me ,意为修理我。如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。

文档注释

def Say(info):
    '''
    	@author 刘博源
    	@date 2000 / 01 / 01
    	@param info 打印的数据信息
    	@desc 打印参数数据
    	@return None 不存在的返回值
    '''
    print(info)
  

使用命令行命令: python -m pydoc -w FileName 可以将代码转化成 HTML 文档

在这里插入图片描述

使用文档注释可以将完善的代码进行一个文档的打包以后其他程序员包括自己在使用该代码的可以查看文档进行了解各个函数对象的作用以便于后继的开发

python -m pydoc -w 可以对整个模块也就是一个代码 文件夹进行文档的生成 访问链接 https://blog.csdn.net/weixin_39519741/article/details/110553171 可以了解更多

其他注释

**HACK:**英语翻译为砍。如果代码中有该标识,说明标识处代码我们需要根据自己的需求去调整程序代码。

**XXX:**如果代码中有该标识,说明标识处代码虽然实现了功能,但是实现的方法有待商榷,希望将来能改进,要改进的地方会在说明中简略说明。

变量的赋值

a = 1 # 简单的赋值 a 可以是任意的类型
b: int = 23 # 指定类型的赋值 b 只能是 int 的类型
c , d = 3 , 4 # 多个值进行赋值 c = 3 d = 4
e , *g , f = 23, 31, 12, 14 # 解压赋值 e = 23 g = [31,12] f = 14
h = ... # 只申明不赋值

Python 内置变量类型

  • str - 字符串
  • int- 整数
  • bool - False True None
  • float - 浮点类型
  • ... - 省略号类型
  • list - 列表
  • tuple - 元组
  • set - 集合
  • dict - 字典
  • function - 函数
  • class - 对象

列表

列表就是 长度自由 可以存放任意类型变量 的数组 使用中括号定义

a = [1,2,4,5,6]

'''
序号	方法
list 代表列表对象
1	list.append(obj)
在列表末尾添加新的对象
2	list.count(obj)
统计某个元素在列表中出现的次数
3	list.extend(seq)
在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
4	list.index(obj)
从列表中找出某个值第一个匹配项的索引位置
5	list.insert(index, obj)
将对象插入列表
6	list.pop([index=-1])
移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
7	list.remove(obj)
移除列表中某个值的第一个匹配项
8	list.reverse()
反向列表中元素
9	list.sort( key=None, reverse=False)
对原列表进行排序
10	list.clear()
清空列表
11	list.copy()
复制列表
'''
a.append("Value")

range

使用 range(a,b,c) 可以快速生成一个 a~b (不包含 b)的 生成器 使用 list() 可以将生成器转化成列表 c 是步长

list_1_100 = range(1,101)
list_1_100 = list(list_1_100)
print(list_1_100)

list_1_100_2 = range(1,101,2) # 每隔2取一个值
list_1_100_2 = list(list_1_100_2)
print(list_1_100)
>>> a= list(range(100,0,-1))
>>> a[0]
>>> a[-1]
1
>>> a[-2]
2
>>> a[0:10]
[100, 99, 98, 97, 96, 95, 94, 93, 92, 91]
>>> a[0:10:2]

字符串

字符串没什么好讲的 使用 '' 或者 “” ,有趣的是 python 的机制

name = "吕小布"
age = 12
job = "学生"
print(f'我叫{name},是一名{age}的{job}')

与 JAVA 不同的是 python 的字符串不能与 int 链接 需要将 int 转成字符串也就是使用 str()​函数将 int 转换成字符串 但是如果你使用上述的代码可以不使用 + 就可以链接字符串与 int

元组

元组就是不能 增删改 的列表 但是可以查询 这样做是为了安全 使用 ( ) 定义

使用 tuple()​函数可以将列表转化成元组, 使用 list()​ 函数可以将元组转化成列表

字典

以键值对组成的列表,使用 { } 定义

student = {"name":"XXX","age":12,"info":"一名学生"}
print(f"我是{student['name']},{student.get('info')},今年{student['age']岁了}")

三目运算符

b = int(input("输入数字"))
a = b if 2 > b else 2 

流程控制

if

if a == b
    xxx
  
elif (c == d):
    xxxx
    if xxx:
        xxx
	xxxx
else:
    xxxxx

while

while (a > 3):
    xxxx
	a 

for

for 在 python 中只能进行 遍历 的操作

for i in [1,2,3]:
    print(i)
  
for i,b in zip(range(1,101),range(100,0,-1)):
    print(f"{i}:{b}")
  
student = {
    "name" : "XXX",
    "age" : 12,
    "info" : "一名学生"
}
for k,v in student.items():
    print(f"{k}:{v}")

字符串的遍历

for i in "I love You":
    print(i)

对象及高级编程

函数

python 的函数可以单独定义在方法外部 也可以定义在对象里面 在对象的函数自然叫做方法

def myfunc():
    print("这是无参数 无返回值对象的函数")

def myfunc_v2(value):
    print("这是 带形式参数 的函数")

def myfunc_v3(value=3):
    print("这是 带默认值 的函数")

def myfunc_v4():
	"""
	@desc 这是 带返回值 的函数
	"""
    return 1

def myfunc_v5() -> int:
    """
    @desc 这是指定类型的返回值的函数
    """
    return 1

def myfunc_v6():...
    """
    @desc 这是什么也不执行的函数
    """

async def myfunc_V7():
    """
    @desc 这是协程函数
    """

def myfunc_v8():
    """
    @desc 这是外函数
    """
    def inner():
        """
        @desc 这是内函数
        """
        ...
# myfunc_v8 总称 闭包函数

装饰器是 python 的神器之一,与之前的 range 生成器与后续学习的迭代器 描述器 都是缺一不可的神器

python 可以利用闭包函数来创建装饰器

import time

def 装饰器(函数参数):
    def 内函数():
        开始时间 = time.time()
        参数函数()
        结束时间 = time.time()
        耗时时间 = 结束时间 - 开始时间
        print(耗时时间)
        return 1
    return 内函数

@装饰器
def myHello():
    time.sleep(3)

a = myHello()
print(a)

____________________________________________________________________________________
import time

def TimeSet(func):
    def __inner__():
        start = time.time() # 获取当前的时间轴
        func()
        end = time.time() # 获取当前时间轴
        print(end - start) # 消耗的时间
    return __inner__

@TimeSet
def helloWorld():
    for i in range(10000):
        print(f"---- 在执行{i} ----")

helloWorld()

func.__name__​代表获取函数的名称

装饰器的内容漫长可以去 https://www.liaoxuefeng.com/wiki/897692888725344/923030163290656 自学学习更多的只是

在 python 中 包就是目录 如果包里面有 python 文件那么这个包就是一个库

对象

Python 使用 class 定义对象 __init__()​ 函数 创建构造器方法

class Student:
    """
    DESCRIPT
    	学生类
    AUTHOR
    	刘博源
    DATE
    	2020/10/12
    """
    def __init__(self,name:str,age:int)->None:
        self.name = name
        self.age = age
    
    def Work(self):
        print("学生在学习")

Stu = Student("xxx",12) # 使用()可以直接创建一个对象 不需要new ()里面需要传入构造器的参数

前缀是 self. 就是对象的属性,他大多在构造器中进行实现(当然也可以出现在其他的地方), self 相当于 Java 的 this 在对象内部创建一个虚拟的对象,在对象里定义的方法没有特殊的装饰器做标致的时候,这个方法的第一个参数就是 self 这个 self 不是平时我们所见的形式参数,他只是告诉我们这个方法是属于 对象 的,如果第一个不是 self 那么在后期使用这个方法的时候会有错误显示

PS: python 的对象没有方法重载的概念 如果同一个方法有不同的参数可以通过默认值的形式进行设置 但是可以进行重写

静态方法的作用

  • 工具集的创建
  • 函数类的创建

特殊的装饰器方法


class myClass:
    """
    DESCRIPT
    	测试类 无实际意义
    AUTHOR
    	刘博源
    DATE
    	2021/10/12
    """
    @classmethod
    class classMethod():
        """
        @desc 类方法
        """
        print("这是类方法")
myClass.classMethod()

使用了 python 内置的 @classmethod 装饰器可以将方法作为类的方法,我们之前有说过,无装饰器且第一参数是 self 的方法是对象方法,对象的方法需要在 class 外部创建一个对象 也就是实例化 然后在使用对象的方法 而是用类对象可以直接使用 类名.方法()​的形式进行调用,但是类方法不能使用对象的方法与成员否则我们不能将这个方法作为类方法

继承

class Student:
    pass

class myClass(Student):
    """
    DESCRIPT
    	测试类 无实际意义
    FATHER_CLASS
    	Student - 非抽象父类
    AUTHOR
    	刘博源
    DATE
    	2021/10/12
    """
    xxx
myClass.classMethod()

魔术方法 魔法方法

方法名被两个下划线包围的是 python 内置的方法

  • __init__
  • __del__
  • __str__

PYTHON 的后续学习

python 是开源免费的脚本语言 它内置了许多优秀的库也就是模块 导致 python 成为了程序员们中不可多得的瑞士军刀 他对 Java 的相合性较高可以混合使用开发对我们之后的工作也有极大的优势

在这里插入图片描述

// Java可以使用python进行运行
import org.python.core.PyFunction;
import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.util.PythonInterpreter;
 
public class Java_Python_test {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.execfile("D:\\add.py");
 
        // 第一个参数为期望获得的函数(变量)的名字,第二个参数为期望返回的对象类型
        PyFunction pyFunction = interpreter.get("add", PyFunction.class);
        int a = 5, b = 10;
        //调用函数,如果函数需要参数,在Java中必须先将参数转化为对应的“Python类型”
        PyObject pyobj = pyFunction.__call__(new PyInteger(a), new PyInteger(b)); 
        System.out.println("the anwser is: " + pyobj);
    }
}

序列化

将 程序的内容 存入文件叫做序列化的操作,相反的 将文件的内容读入程序叫做 反序列化

文本文件的操作

以下的方法只能存储字符串

Hello = "123"
f = open("FileName.txt",'w') # w 代表 写权限
f.write(Hello)
f.close() # 关闭文件句柄

ReadHandler = open("FileName.txt",'r') # r 代表 写权限
FileReadResult = ReadHandler.read()
print(FileReadResult)
ReadHandler.close()

上述的代码过长 可以使用上下文管理器进行优化

with open("FileName.txt",'r') as f:
    result = f.read()

json 与 pickle

import json,pickle
class MyClass:
    name = "lby"

with open("xxx.pic",'wb') as f:
    pickle.dump(MyClass(),f)

with open("xxx.pic",'rb') as f:
     pic = pickle.load(f)
print(pic.name)

with open("xxx.pic",'w') as f:
    json.dump([1,2,4,5],f)

with open("xxx.pic",'r') as f:
     pic = json.load(f)
print(pic)

列表推导式

_list3 = [i for i in range(100)]
_list2 = [random.randint(1,100) for i in range(100)]
_list = [i for i in range(100) if i % 2 == 1]
print(_list)

异常捕捉

# 抛出异常
raise TypeError("xxx")
class 错误(Exception):
    def __init__(self,val : str):
        pass
raise 错误("自定义错误")

try:
    a = 1 # try抓到错误 
except:
    print("出现错误") # try抓到错误 就执行
else:
    print("else 块") # try没有抓到错误 就执行
finally:
    print("处理完毕") # 结束执行

异步 - 多线程函数实现

import threading
def Thread1(val):
    while 1:
        print("*" * 40)
        print("A 执行中")
def Thread2():
    while 1:
        print("*" * 40)
        print("B - 执行中")
t = threading.Thread(target=Thread1,args=(1,))
t2 = threading.Thread(target=Thread2)
t.start()
t2.start()

使用多线程进行爬虫的处理

from urllib.request import urlopen
import re
import random
import threading  # 处理中等 |小 数据  - 资源少
import multiprocessing  # 处理特大数据 - 块

re_rule = '''<div class="inn-archive__item__container inn-card_painting__item__container">
        <a
            href="(.*?)"
            target="_blank"
            class="inn-archive__item__thumbnail__container inn-card_painting__item__thumbnail__container inn-card__thumbnail__container inn-card__thumbnail__container_painting"
        >
            <img
    class="inn-archive__item__thumbnail__img inn-card_painting__item__thumbnail__img inn__thumbnail__img"
    loading="lazy"
    src="(.*?)"
    alt="(.*?)"
    width="200"
    height="300"
/>'''


def Save(picurl, afterName):
    print(f"这个是{picurl},他的是{afterName}")
    with open("./pic/" + str(random.random()) + "." + afterName, 'wb') as f:
        f.write(urlopen(picurl).read())


def downLoad(list):
    for i in list:
        url: str = i[1]
        url_after = url.split('.')[-1]
        Save(url, url_after)


def GetPageImage(page):
    url = f"https://www.jiligamefun.com/category/photo/page/{page}"
    print(f"----- 爬取第{page}页的数据 -----")
    response = urlopen(url)
    html = response.read().decode()
    # print(html)
    img = re.findall(re_rule, html)
    print(img)
    downLoad(img)


# for i in range(1,29999):
#     GetPageImage(i)
def Thead(s, e):
    print(f"这是一个爬取{s} ~ {e}的爬虫")

    for i in range(s, e + 1):
        GetPageImage(i)


start = 1
end = 20
for i in range(20):
    t1 = threading.Thread(target=Thead, args=(start, end))
    t1.start()
    start += 20
    end += 20
    import time

    time.sleep(1)

jieba 库

使用实例

需要: pip install jieba

import jieba

listA = jieba.cut("python是一个计算机语言")
print(listA)

分词

jieba.cut 方法接受两个输入参数: 1) 第一个参数为需要分词的字符串 2)cut_all 参数用来控制是否采用全模式

jieba.cut_for_search 方法接受一个参数:需要分词的字符串,该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细

注意:待分词的字符串可以是 gbk 字符串、utf-8 字符串或者 unicode

jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),也可以用 list(jieba.cut(...))转化为 list

在这里插入图片描述

Flask

后端

from flask import Flask, render_template, request

APP = Flask(__name__)

@APP.route("/")
def index():
    return "<h1>欢迎</h1>"

@APP.route("/index")
def index2():
    return render_template("index.html")

if __name__ == '__main__':
    APP.run(host='192.168.1.102', port=9000,debug=True)

前端

<!DOCTYPE html>


<!-- MVC - V 试图 -->
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">

	<title></title>
</head>
<body>
	<table>

		<tr bordercolor="red">
			<td>1</td>
			<td>2</td>
			<td>3</td>
		</tr>

	</table>
	<h1>xx</h1>
	<img src="{{url_for('static',filename='img/back.jpg')}}" > 
	<!-- 渲染 -->

</body>
</html>

static 目录 存储静态文件
templates 目录 存储

get

  • 缺点:他会在 URL 内部显示传入的表单元素信息
  • 优点: 快
  • 使用:request.args

POST

  • 使用:request.form
  • 优点 安全
  • 缺点 慢

数据库与配置文件

InI 文件

ini 文件的内容 nosq

[Setting] 
; 设置配置文件
questioncount = 400
asktime = 17940
wordcount = 100

[User]
; 用户配置
RemeberPwd = 'none'


python 的操作

import configparser


class iniManager:
    def __init__(self,fileName):
        self.fileName = fileName
        self.path = self.fileName
        self.iniHandle = configparser.ConfigParser() # 生成句柄
        self.iniHandleReadPermission = self.iniHandle.read(self.path)

    def getDas(self):
        return self.iniHandleReadPermission

    def getValue(self,Option,KeyName):
        return self.iniHandle.get(Option,KeyName)

    def setDas(self,Option,Key,Value):
        self.iniHandle.set(Option,Key,Value)
        self.iniHandle.write(open(self.path,'w'))

if __name__ == '__main__':
    test = iniManager('Setting.ini')

    print(test.setDas('Setting','AskTime','123'))

本地数据库 SQLite

SQLite 是一个轻量级的数据库管理系统(DBMS) 常用:安卓 Windows 程序的开发 他不能存放在网络中 只能在本地进行存储 需要下载一个程序:

SQLite.zip
链接:http://note.youdao.com/noteshare?id=8413cae7fe5329a63a5fa34b08e33857&sub=ED38F980F78249F987CE696B9373343B

这个程序有效的可以管理 SQLite SQLite 的 SQL 语句与 MySQL 的 SQL 语句类似

网络数据库 Mysql

import pymysql

#region 这里的代码用于干链接数据库的事情

conn=pymysql.connect(host = '127.0.0.1' # 连接名称,默认127.0.0.1
,user = 'root' # 用户名
,passwd='password' # 密码
,port= 3306 # 端口,默认为3306
,db='test' # 数据库名称
,charset='utf8' # 字符编码
)
cur = conn.cursor() # 生成游标对象

sql="select * from `student` " # SQL语句
cur.execute(sql) # 执行SQL语句

#endregion

描述符 (了解)

这里是一个比较优秀的描述符文章以下的笔记解释一个转载

如下列的代码

class Age:
    def __get__(self,obj,objtype=None)
        return 10

class Student:
    age = Age()

if __name__ == "__main__":
    print(Student().age)

允许把一个类属性(不是对象属性)托管给一个类这个属性就是描述符属性,所以描述符的赋值实际上是一个绑定属性

class PersonType:
    def __get__(self,obj,objtype=None):
        print(obj.name)
        if obj.name == "xxx":
            return "上单"
        elif obj.name == "xxxx":
            return "法师"
        else:
            return "Unknow Type"
class Student:
    _type = PersonType()
    def __init__(self,name):
        self.name = name

if __name__ == "__main__":
    print(Student("xxxx")._type)

当然的 类的描述符对象不止有__get__方法,描述符内部的方法是不能随便定义的也需要实现一下几个方法:

__get__(self, obj, type=None) -> value
__set__(self, obj, value) -> None
__delete__(self, obj) -> None

只要实现了以上方法的其中一个,这个方法就是一个描述符对象

另外,描述符又可以分为「数据描述符」和「非数据描述符」:

只定义了 __get___​,叫做非数据描述符
除了定义 __get__ ​​之外,还定义了 __set__​或 __delete__​,叫做数据描述符

# coding: utf8

class Age:

    def __init__(self, value=20):
        self.value = value

    def __get__(self, obj, type=None):
        print('call __get__: obj: %s type: %s' % (obj, type))
        return self.value

    def __set__(self, obj, value):
        if value <= 0:
            raise ValueError("age must be greater than 0")
        print('call __set__: obj: %s value: %s' % (obj, value))
        self.value = value

class Person:

    age = Age()

    def __init__(self, name):
        self.name = name

p1 = Person('zhangsan')
print(p1.age)
# call __get__: obj: <__main__.Person object at 0x1055509e8> type: <class '__main__.Person'>
# 20

print(Person.age)
# call __get__: obj: None type: <class '__main__.Person'>
# 20

p1.age = 25
# call __set__: obj: <__main__.Person object at 0x1055509e8> value: 25

print(p1.age)
# call __get__: obj: <__main__.Person object at 0x1055509e8> type: <class '__main__.Person'>
# 25

p1.age = -1
# ValueError: age must be greater than 0

在这例子中,类属性 age 是一个描述符,它的值取决于 Age 类。

从输出结果来看,当我们获取或修改 age 属性时,调用了 Age 的 getset 方法:

当调用 p1.age 时,get 被调用,参数 obj 是 Person 实例,type 是 type(Person)

当调用 Person.age 时,get 被调用,参数 obj 是 None,type 是 type(Person)

当调用 p1.age = 25 时,set 被调用,参数 obj 是 Person 实例,value 是 25

当调用 p1.age = -1 时,set 没有通过校验,抛出 ValueError

其中,调用 set 传入的参数,我们比较容易理解,但是对于 get 方法,通过类或实例调用,传入的参数是不同的,这是为什么?

总结

首先,我们从一个简单的例子了解到,一个类属性是可以托管给另外一个类的,这个类如果实现了描述符协议方法,那么这个类属性就是一个描述符。此外,描述符又可以分为数据描述符和非数据描述符。

之后我们又分析了获取一个属性的过程,一切的入口都在 getattribute 中,这个方法定义了寻找属性的顺序,其中实例属性优先于数据描述符调用,数据描述符要优先于非数据描述符调用。

另外我们又了解到,方法其实就是一个非数据描述符,如果我们在类中定义了相同名字的实例属性和方法,按照 getattribute 中的属性查找顺序,实例属性优先访问。

最后我们分析了 function 和 method 的区别,以及使用 Python 描述符也可以实现 property、staticmethod、classmethod 装饰器。

Python 描述符提供了强大的属性访问控制功能,我们可以在需要对属性进行复杂控制的场景中去使用它。

想想

  • 怎么样使用属性描述符实现只读属性?
  • 怎么样使用属性描述符实现将方法变成属性?
  • 怎么样将实例方法变成类方法?

SOCKET

套接字初认识

套接字可以实现局域网内部的通信,常见的有两种模式 —— UDP 匿名聊天模式 与 TCP CS 聊天模式
使用套接字编程大约的步骤是:

  • 创建套接字
  • 套接字的准备(绑定 IP_Port)
  • 收发数据

UDP

使用 SOCK_DGRAM 创建 UDP 的套接字

# 接受端
import socket
# A
soc = socket.socket(
    socket.AF_INET ,# 使用的IPv4进行通信
    socket.SOCK_DGRAM # UDP 协议
)
hostname = socket.gethostname()
port = 12222
soc.bind(('192.168.1.102',port))
print(hostname)
print("开始接受消息")
msg, recved = soc.recvfrom(1024)
print(msg.decode('gbk'))
soc.sendto("我是谁?".encode('gbk'),recved)


# 发送端

# B
import socket
soc = socket.socket(
    socket.AF_INET ,# 使用的IPv4进行通信
    socket.SOCK_DGRAM # UDP 协议
)
print("开始发送消息")
soc.sendto("消息".encode('gbk'),("192.168.1.102",12222))

msg,ip_port = soc.recvfrom(1024)
print(msg.decode('gbk'))

基于上述的代码我们可以写个多线程的聊天室程序

import socket
import threading

def RECV():
    global tx_ip,soc
    while 1:
        msg,_ = soc.recvfrom(1024)
        print(msg.decode('gbk'))

def SEND():
    global tx_ip,soc
    while 1:
        msg = input("你的消息是什么?")
        soc.sendto(msg.encode('gbk'),tx_ip)

soc = socket.socket(
    socket.AF_INET,  # 使用的IPv4进行通信
    socket.SOCK_DGRAM  # UDP 协议
)
choose = input("请选择你的选项\n 1创房间 2连接房间")
tx_ip = ...
if choose == "1":
    soc.bind((("192.168.1.102"), 2222))
    _,tx_ip = soc.recvfrom(1024)
    print("有角色加入")

else:

    ip = input("输入房主的IP")
    port = 2222
    tx_ip =(ip,port)
    soc.sendto(b"", tx_ip)
    print("连接到指定房间  ")

t1 = threading.Thread(target=RECV)
t2 = threading.Thread(target=SEND)

t1.start()
t2.start()

t2.join()
t1.join()

TCP

服务端

import socket

selfIp = ('127.0.0.1', 31500)
soc = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)
soc.bind(selfIp)
soc.listen(5)
# 监听 (开启网络服务)

while 1:
    cli,ret = soc.accept() # 等待别人加入
    print("角色加入")
    print(ret)

    result = cli.recv(512)
    print(result)
    cli.send("收到了".encode("gbk"))

客户端

import socket
 
targetIp = ('127.0.0.1', 31500)  # 服务器的地址
soc = socket.socket(
    socket.AF_INET,
    socket.SOCK_STREAM
)
soc.connect(targetIp) # 加入服务器
soc.send(b"HelloWorld")
print(soc.recv(512).decode("gbk"))

HTTP 协议

HTTP 协议 是从 TCP 协议转化而来 是一种超文本传输协议

我们创建一个 TCP 服务器等待浏览器的链接 链接之后 TCP 服务器会获取一段 HTTP·请求报文·

GET / HTTP/1.1
Host: 127.0.0.1:12222
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: csrftoken=FkQjDNdWBiXLbZem9ikHLMhp8jbDpYhHhdEz6PaQ8J8Oc4PI0nCLFsiEeocX7jnt
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1

TCP 套接字实现服务器

import socket as soc

HTTP_RESPONSE = """
"""
M = 0


def service_client(cso: soc.socket):
    """
    客户端服务
    :return:
    """
    global M
    M += 1
    request = cso.recv(1024)  # 接受浏览器请求
    response = "HTTP/1.1 200 OK\r\n"
    response += "\r\n"
    response += ""
    response += f"<h1>HelloWorld:{M}</h1>"
    cso.send(response.encode('utf-8'))
    cso.close()


def main():
    """
    总体控制
    :return:
    """
    so = soc.socket(soc.AF_INET, soc.SOCK_STREAM)
    so.bind(("", 12333))
    so.listen(128)
    while True:
        cso, address = so.accept()
        service_client(cso)
    
    so.close()


if __name__ == '__main__':
    main()

TCP 三次握手 四次挥手

三次握手

C(客户端)与 S(服务端)在真正的通讯之前有三次简单的握手

  • C -> S: syn

    • 客户端给服务器发送第一次握手 让服务器准备资源
  • S -> C:

    • S 准备 资源 告诉客户端准备好了 ack
    • 并让 C 也准备好资源 ync
    • (此时对于 C 而言 服务器已经为他准备好了一切)
  • C -> S:

    • 客户端也准备好了 我们开始通讯吧! (对于服务器而言,客户端也准备好了 双向奔赴的通信开始了捏)ack

    所以 客户端套接字的 connect 理论上是阻塞的 只有三次握手执行完毕 connect 才会解堵塞

四次挥手

  • C -> S

    • C 不想与 S 进行发数据了
  • S -> C

    • 我收到了 S -> C
  • S -> C

    • S 不在接受任何数据(解堵塞:recv)

      • 这里的接受数据只是应用层的问题 系统底层还需要进行挥手
      • 这时候如果 recv 被接堵塞 那么收到的将是 None
      • 两次挥手完毕之后 会在此处设置一个超时时间,等待若干秒 如果若干秒后 C 的第四次挥手没有到来 我会重发
  • C->S

    • C 也不再收 S 的数据了
    • S 与不在发 C 的数据了
    • 会在此处设置一个超时时间 是 2msl(大约 2~5 分钟 6) 也就是两倍的数据包最长存活时间

为什么服务器会分两次发送给 C 进行挥手?

S 的第一次挥手只是表示我收到了客户端请求了,是还没有关闭服务器中的套接字的

S 的第二次挥手表示我也关闭了套接字

如果说在通讯的时候是服务端发送的 close, 那么最后一个 2msl 的超时等待会让服务器来进行 这简直太糟糕了 因为在 2msl 的时间内,这个端口一直是被占用的 导致了 close 之后服务端不能被重启 所以一般挥手的开始是从客户端进行 客户端在通讯的时候是没有进行端口绑定的 下一次客户端可以使用任意的端口继续运行 但是服务端端口是被绑定的 所以第二次是会被占用

server_socket.setsocketopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

可以解决这个问题

实现网站

import socket as soc
import re
HTTP_RESPONSE = """
"""
M = 0


def service_client(cso: soc.socket):
    """
    客户端服务
    :return:
    """
    global M
    M += 1

    request = cso.recv(1024).decode('gbk')  # 接受浏览器请求

    route = re.findall("GET (.*?) HTTP/1.1",request)
    print(route)

    if route:
        route = route[0]
    else:
        route = "/404.html"
    route = route.replace("/",'./HTML/')
    try:
        with open(route,'r') as f:
            html = f.read()
    except PermissionError:
        with open("HTML/404.html","r") as f:
            html = f.read()
    print(request)
    print("_"*40)
    response = "HTTP/1.1 200 OK\r\n"
    response += "\r\n"
    response += ""
    response += html

    cso.send(response.encode('utf-8'))
    cso.close()


def main():
    """
    总体控制
    :return:
    """

    so = soc.socket(soc.AF_INET, soc.SOCK_STREAM)

    so.bind(("", 12333))

    so.listen(128)
    while True:
        print("___wait Client")
        cso, address = so.accept()
        service_client(cso)

    so.close()


if __name__ == '__main__':
    main()

pandas

简介

  • 基于numpy的工具

  • 高效提供了大量库和一些标准的数据模型

    相当于Numpy的一维数组,但是与普通的一维数组不同,Series支持对数据的自定义标签也就是index(索引),通过索引可以访问Series的元素

Series

索引

简单的创建

import pandas as pd
sel = pd.series()
print(sel)

``

我们发现当我们没有创建索引的时候回自动创建一个类似于下标的索引,从0开始,

索引的设置

使用data设置值,index设置索引

import pandas as pd 
sel = pd.Series(data=[5,4,3,2],index=[1,2,3,4])
print(sel)

通过数组对象的index属性可以直接获取索引

value 可以获取值

import pandas as pd
sel = pd.Series(data=[1,2,3,4,5],index=list('abcde'))
print(sel.values) # 获取值
print(sel.index) # 获取索引
print(list(sel.iteritems())) # 获取索引与值的键值对列表

[1 2 3 4 5]Index(['a', 'b', 'c', 'd', 'e'], dtype='object')[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]

字典转Series

import pandas as pd
pd_dict = {"red":20,"blue":40,"green":40}
sel = pd.Series(pd_dict)
print(sel)

red 20blue 40green 40dtype: int64

通过 索引|位置 得到值

通过 数组[索引] 可以获取值

import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
print(sel["red"])

20

位置也可以获取值,位置实际就是从0开始的数组下标索引

通过索引|位置列表组可以获取数组的多个值

import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
print(sel[['red','blue']])

red 20blue 40dtype: int64

修改index值

  • 使用index修改数组本身索引
import pandas as pd 
sel = pd,Series({"red":20,"blue":40,"green":40})
sel.index=['r','g','b']
  • 使用reindex修改数组索引获取新数组
import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
ret = sel.reindex(['r','g','b'])

如果index超出了值的长度,那么默认补全NaN给超出的部分

drop 删除值

使用drop删除指定索引的数据,传入列表就是删除多个

import pandas as pd
sel = pd.Series({"red":20,"blue":40,"green":40})
ret = sel.drop(["red","green"]) # 删除索引red与green

一维数组的计算

Series支持numpy的计算,且Series的计算与index进行对应即映射,相同的index进行运算,如果运算的一方无法与另一方对应那么运算的结果该index的值就是NaN

import pandas as pd
selA = pd.Series({"banana":12,"apple":41,"orange":100})
selB = pd.Series({"banana":42,"apple":11,"gruy":100})
selC = selB + selA
print(selC)

apple 52.0banana 54.0gruy NaNorange NaNdtype: float64

DataFrame

DataFrame是一个pandas的二维数组 所以他有两个索引一个是行索引一个是列索引

基础使用

创建DataFrame(DF)

import pandas as pd
import numpy as np
DF_A = pd.DataFrame(np.random.randint(0,10,(4,4)))
# 将四行四列的随机numpy数组转化成DF

0 1 2 30 8 2 2 31 9 4 8 42 4 4 2 53 3 9 5 8

修改两个索引的方法

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randint(0,100,(4,4)))
# 也可以使用index参数与columns参数修改

df.index = [1,2,3,4] # 行索引
df.columns = list("ABCD") # 列索引

print(df)

字典转DF

通过字典生成的DF,字典的键会自动成为列索引,其值会成为该列的数组元素

import pandas as pd
dict_pd = {"banana":[10,12,9],"orange":[99,121,160]}
df = pd.DataFrame(dict_pd,index=["超市A","超市B","超市C"])
print(df)

banana orange超市A 10 99超市B 12 121超市C 9 160

当然我们可以值为Series的字典来创建数组

常用属性方法

import pandas as pd

data = pd.DataFrame(
    {"banana": [10, 12, 9], "orange": [99, 121, 160]},
    index=["超市A", "超市B", "超市C"]
) 
index = data.index.tolist()  # 将DF的index收集成列表
columns = data.columns.tolist()  # 将DF的列索引收集成列表
shape = data.shape  # 获取数组而定行与列
print(data.shape)  # DF 的维度
print(shape)
print(data.values)  # DF数据查看
print(data.info)  # 数据的信息
print(data.head(2))  # 查看前2行,默认是前五行
print(data.tail())  # 显示后几行
  • rename()

    • 可以修改DF的 index 与 colunms的值
def rename_index(x):
    return str(x + 1)

def rename_columns(x):
    return x.replace("User", "")
a = df.rename(index=rename_index, columns=rename_columns)
print(a)

A B1 1 22 2 33 4 54 5 65 6 1

使用键值对来修改单一的index 与 columns 也是可以的

  • set_index

    • 将指定列的值赋值给index
b = df.set_index("UserA",drop=False)
print(b)

定位

获取指定列

import pandas as pd
data = pd.DataFrame(
    {"banana": [10, 12, 9], "orange": [99, 121, 160]},
    index=["超市A", "超市B", "超市C"]
)
col_banana = data["banana"]
# 获取指定的列 返回值DF
print(data[["banana","orange"]])
# 获取多列返回值DF
print(df.loc[:,["UserA"]]) # 获取UserA列
ret = data[0:1]
# 获取一行

指定行

使用loc定位行

import pandas as pddf = pd.DataFrame({"UserA":[1,2,4,5,6],"UserB":[2,3,5,6,1]})data_userA_col = df["UserA"].loc[1:4] # 获取UserA列的 1—4index 的值print(data_userA_col)

添加列与行

运算方法

import pandas as pd
df = pd.DataFrame({"UserA": [1, 2, 4, 5, 6], "UserB": [2, 3, 5, 6, 1]})# 以后数据源就是这个了

# 追加
df["UserC"] = [4,2,3,4,5] # 长度需要与row相同

# 指定插入
list_col = df.columns.tolist() # 将列转为列表
list_col.insert(2,"UserD") # 向列表插入数据
e = df.reindex(columns=list_col) # 修改数组的列 reindex 可以对原行列索引重新构建索引值
e["UserD"] = [4,1,5,4,1]
print(e)

# 指定插入II
f = df
f.insert(2,"userD",[24,5,1,2,4])
print(f)

append

  • 将两个DF合并成一个DF
dataA = pd.DataFrame({"name":["刘博源"],"Tel":[1271124111]})
dataB = pd.DataFrame({"name":["李三"],"Tel":[1212341111]})
data_sum = dataB.append(dataA,ignore_index=True)
print(data_sum)
# ignore_index:是否忽略index,如果是True值,那么添加的时候就忽略添加数据的index值,而是递增之前的index如果是ignore_index=False 那么就会保留之前的index(视频有个错误,,)f

行列合并

ret = pd.concat([df1,df2],axis=1) # 按行拼接 X + X 
'''
{——}{——}
'''
ret2 = pd.concat([df1,df2],axis=0) # 安列拼接
'''
{--}
{--}
'''

数据处理

滤除缺失值

  • dropna()
import pandas as pd
import numpy as np

#Series
df = pd.Series([np.nan,12,np.nan])
print(df)
df_noNan = df.dropna()
df_notNan = df.notnull() # 广播是否为Nan

#Pandas
df1 = pd.DataFrame([[1,2,4],[4,1,np.nan],[np.nan,13,1]])
df_clear = df1.dropna()
‘’‘
dropna params:
    axis
    	默认 : 过滤行
    	axis = 1:过滤列
    how
        判断方式 为 any 时 就是遇见Nan就删除整行|列
        判断方式 为 all 时 就是全部Nan就删除整行|列
    thresh
    	保留 至少hresh个非nan的 行|列
’‘’
print(df_clear) # 过滤所有包含Nan值的行


填充空值

  • fillna()
df1.fillna(1) #  将所有为Nan的Value替换成1
'''
inplace:是否在原数组修改不返回值,默认False
'''

df1 = pd.DataFrame([[1,2,4],[4,1,np.nan],[np.nan,13,1]],columns=[1,2,3],index=list("abc"))
df1.fillna({1:3,2:4,3:4})
# 根据columns进行字典的填充

# 指定列(位置)填充
df1.fillna([:,1].fillna(5,inplace=True))

# 前后值填充
ret = df.fillna(method="bfill") # 使用后面(下一行同列)的值填充
ret = df.fillna(method="ffill") # 使用前面(上一行同列)的值填充
'''
params:
	limit:
		限制的填充的行数
	axis:
		改变填充的方向 
			1:
				同行不同列的填充
			0:
				同列不同行的填充
	
'''

去重

df = pd.DataFrame({"A":[1,2,1,5,6,1],"B":[1,2,1,5,1,2]})
print(df)

isRepeat = df.duplicated() # 判断是否重复(行与行之间的)
print(isRepeat)

dropedRepeat = df.drop_duplicates() # 删除重复行 
print(dropedRepeat)
# 可以传入一个列表参数给drop_duplicates代表指定的列的重复判断
'''
params:
   	inplace:修改原数组不返回新数组
'''

数据合并

join()

df3 = pd.DataFrame({"Red": [1, 4, 5], "Blue": [2, 4, 5], "Yellow": [1, 5, 6]},index=list("123"))
df4 = pd.DataFrame({"Gury": [1, 4, 5], "Green": [2, 4, 5], "Purple": [1, 5, 6]},index=list("124"))
print(df3.join(df4))
'''
	将 df3 与 df4 进行横向的合并 
	index会默认以df3 join的调用者为基础 

    参数df4多余的idnex在合并时被忽略
    没有的index合并值为填充为NaN
    params:
    	how:如何合并
    		-left 左合并
    			默认的合并法则
    	
    		- right 右合并
    			index会以 df4 参数为基础进行合并 
  
    		- outer 全合并
    			index 会将进行合并的两个数组全部的索引为基础进行合并
'''


merge()

pd_char = pd.DataFrame(data=[[1001,"向老表","A"],[1002,"刘天皇","B"],[1003,"佘东阳","C"],[1004,"岳顺江","C"]],columns=["编号","名称","工程编号"])
pd_project = pd.DataFrame(data=[["A","数据处理集群管理"],["B","全栈开发"],["C","前端开发&数据可视化"]],columns=["工程编号","名称"])

pd_sum = pd.merge(pd_char,pd_project,on="工程编号",suffixes=['_人物','_工程'],how="inner") # on 相当于链接的键值
'''
how:
    inner :内连接
    left:左连接
    right:有链接
    outer:外连接
suffixes:
    如果链接的两个DF有相同的Columns那么我们就可以给这两个DF的同名索引取后缀,suffixes可以规定两个后缀
'''
print(pd_sum)

编号 名称_人物 工程编号 名称_工程0 1001 向老表 A 数据处理集群管理1 1002 刘天皇 B 全栈开发2 1003 佘东阳 C 前端开发&数据可视化3 1004 岳顺江 C 前端开发&数据可视化

多层索引

我们可能会遇见这种索引

![image-20210414023130871](Pandas.assets/image-20210414023130871.png)

也就是一个大索引可以分为多个小索引(层级索引),多个小索引又有多个具体的值

如何具体去创建这样的数组 我们可以通过以下的代码

Series多层索引数组

import numpy as np
import pandas as pd
Mark_Numpy = np.random.randint(40,100,size=(6,))
Mark_Sum_version_1 = pd.Series(Mark_Numpy,index=['a','a','b','b','c','c']) # 简单的创建
Mark_Sum_version_2 = pd.Series(Mark_Numpy,index=[['a','a','b','b','c','c'],["语文","数学","语文","数学","语文","数学"]]) # 多层索引 创建
print(Mark_Sum_version_2)
print(Mark_Sum_version_2["a","数学"])# 值的定位,注意区分取多值与这个的差别
'''
补充
使用iloc定位的时候因为使用的是位置
索引传入的小标还是最内层的索引 
'''

a 语文 60数学 64b 语文 49数学 94c 语文 79数学 51dtype: int32

DataFrame多层索引数组

这里讨论的多索引是index方面的而不是columns方面的

# 方法一
multilayerDF = pd.DataFrame(
    data=numpy_array,
    columns=["入学成绩","月考成绩","期末成绩"],
    index=[
            ['刘博源','刘博源','刘博源',"向国骑","向国骑","向国骑"],
            ["java",'python','English',"java",'python','English']
           ]
)
print(multilayerDF)

# 方法二
outterIndex = ["刘博源", "刘博源", "刘博源", "向国骑", "向国骑", "向国骑"]  # 外层索引
innerIndex = ["java", 'python', 'English', "java", 'python', 'English']  # 内层索引

mi = pd.MultiIndex.from_arrays([outterIndex, innerIndex])  # 创建索引
df2 = pd.DataFrame(data=np.random.randint(10, 40, (6, 3)), index=[outterIndex, innerIndex])
print(df2)

# 方法三 可以不用格式数组里面究竟有多少个重复的元素
outterIndex = ["刘博源",'向国骑']
innerIndex = ["Python","Java","English"]

mi = pd.MultiIndex.from_product([outterIndex,innerIndex]) # 创建索引
Mpd = pd.DataFrame(np.random.randint(1,100,(6,3)),index=mi)
print(Mpd)
# 利用这种方式创建一个Series试试?

入学成绩 月考成绩 期末成绩刘博源 java 67 82 82python 92 67 97English 87 72 85

向国骑 java 81 75 68python 80 53 94English 84 65 75

多索引数组的取值

# S是一个多层索引Series D是一个多层索引DataFrame

print(S['a']) # 外层索引为a
print(S[['a','b']]) # 外层索引为a,b(多外层获取)
print(S["a","期末"]) # 取a索引的期末子索引

print(D.loc["a"])# 外层索引为a
print(D[['a','b']])# 外层索引为a,b(多外层获取)
print(D["a","期末"])# 取a索引的期末子索引

时间序列

时间序列可以用来处理表示时间的数据

使用date_range()生成有序的时序
单位:
    D   日历上的每一天
    B   工作日的每天
    H   每小时
    T|min   每分钟
    S   每秒
    L|ms    每毫秒
    M   日历日的月底日期
    BM  工作日的月底日期
    MS  日历日的月初日期
    BMS 工作日的月初日期
freq:
    日期偏移量
  
    时间的间隔是一个字符串,格式为:数字+单位
    比如:10D 代表间隔十天
periods:
    固定时期
    取值为整数或者None
    start, end, periods, freq 不能同时存在
# 创建含有时序的DF
data = pd.date_range(start='20190101', end='20200101')  # 创建时序
 
time_dataFrame = pd.DataFrame(
    data=np.random.randint(80,700,(data.size,3)),
    index=data, # 通过时序创建index索引
    columns=["早上","中午","晚上"]
)

# 时序df的检索
year_2019_date = time_dataFrame['2019']  # 获取2019年的数据
year_2019_month_5_date = time_dataFrame['2019-05']  # 获取2019年5月份的数据
year_2019_during_5mTo8m = time_dataFrame.loc['2019-05-01':'2019-08-01']  # 获取五月到8月的数据
'''
这里可以不使用loc:
year_2019_during_5mTo8m = time_dataFrame['2019-05-01':'2019-08-01']  # 获取五月
但是这样会报警告
'''

print(year_2019_during_5mTo8m)


date = pd.date_range(start="2021-04-15",periods=40,freq='2h')
df = pd.DataFrame(np.random.randint(1,40,(date.size,3)),index=date)

during_1pTo5p = df.between_time("1:00","5:00")
print(during_1pTo5p)
# 获取每天在1点到五点的数据

分组聚合

分组

df = pd.DataFrame(columns=["姓名", "订单编号", "金额"],
                  data=[["刘博源", 1, 1020], ["李四", 2, 1000], ["李四", 3, 1050], ["刘博源", 4, 1920], ["向国骑", 5, 1100]])
groupbyName_df = df.groupby("姓名")  # 通过姓名分组
AllGroupsbyName = groupbyName_df.groups  # 通过分组操作分出来的组(键值对出现 键是分组依据 值是对应的索引序列)
print(AllGroupsbyName)

count_group_name_df = groupbyName_df.count()  # 查看分组的分组数与其对应值的数量
for name,Group in groupbyName_df:  # 使用遍历可以对分组结果进行对象解析
    print(name)  # 依次分组的数组名称
    print(Group)  # 以关键字分组的数组小组

Path_df_LiSi = groupbyName_df.get_group("李四")  # 获取按名称分组的李四组

groupNameOnlySeeOrderColumns = df["订单编号"].groupby(df["姓名"])
# 如果只看DF的某列对全DF分组
print(groupNameOnlySeeOrderColumns.get_group("向国骑"))
groupNameAndJinE_df = df.groupby(["姓名",'金额'])

聚合

# 可以对分组后的对象进行聚合函数运算

# 题:通过下列数组求出每个人的总和
df_mark = pd.DataFrame([["刘博源","英语",100],["向国骑","英语",90],["刘博源","python",82],["向国骑","Java",75]],columns=["考生","科目","成绩"])
df_mark_groupByName = df_mark.groupby("考生")
df_mark_avg = df_mark_groupByName.sum()
# 在进行运算的时候不是数值的列 会被清除 但是会保留分组依据列索引 
print(df_mark_avg)

df_mark2 = pd.DataFrame([[1001,"刘博源", "英语", random.randint(1, 100)], [2001,"向国骑", "英语", random.randint(1, 100)],
                        [1002,"刘博源", "python", random.randint(1, 100)], [2002,"向国骑", "Java", random.randint(1, 100)]],
                       columns=["考号","考生", "科目", "成绩"])

df_onlyOperaMark = df_mark2["成绩"].groupby(df_mark2["考生"]) # 只计算成绩列 不去计算考号列
print(df_onlyOperaMark.sum())

df_onlyOperaMark = df_mark2.groupby("考生")["成绩"].sum() # 另一种写法
print(df_onlyOperaMark)

其他的聚合函数

聚合函数 作用
min() 最小值
max() 最大值
sum() 求和
mean() 平均数
std() 标准差
size() 按照groupby的值计算该值的个数 与count函数的区别在于,size函数会计算NAN值,而count函数不会计算NAN值
nunique() 去掉重复值后进行计数
count() 计算个数

agg自定义聚合函数

使用agg可以使用聚合函数

df_grouper.agg("sum") # df.sum()
df_grouper.agg("count") # df.agg()
df_grouper.agg("sum","count") # 多聚合
def peak_range(df):
    return df.max() - df.agg('min')
df_grouper.agg(peak_range)

apply

用处一:可以通过自定义函数来筛选数据

def isSixStar	(value):
    return True if value == 6 else False


df_data = pd.DataFrame(
    [["能天使", "狙击", 6, 241], ["克洛斯", "狙击", 3, 41], ["莫斯提马", "术士", 6, 511], ["月见夜", "近卫", 3, 51], ["安洁莉娜", "辅助", 6, 102]],
    columns=["名称", "类型", "星级", "攻击"])
# df_group_job = df_data.groupby("类型")
is_six_star = df_data["星级"].apply(isSixStar)  # 判断是否为6星人物
print(is_six_star)
df_data["是否为六星"] = is_six_star
print(df_data)
def sort_gat_fristAndSecond(df, columns, wight):
    '''
    通过列排序_从小到大
    :param df: 参与排序的数组
    :param columns:指定列
    :param wight: 前几位
    :return: 数组更具排序前几位列
    '''
    sort_df = df.sort_values(by=columns)
    print(sort_df)


df_temp.groupby("星级").apply(sort_gat_fristAndSecond, columns="攻击", wight=2)

RenPy Python 的游戏开发引擎

RenPy 不是我们学习的大头 他与 PyGame 只是我们开始真正学习的餐前甜点

RenPy 可以开发一款出色的 GalGame(视觉小说)出来 著名的 DDLC(心跳文学社)便是基于这个引擎开发

RenPy 开发对 Python 有良好的的支持但是他的主语言是 RenPy 自己的语言与 Python 相类似使用 Atom 编辑器可以编写这类型的代码 当然使用 VS code 也是可以的

RenPy 是免费的,你可以商用也可以非商用,同时他是跨平台的可以支持 Android Windows Linux 任何的平台开发

下载地址:https://www.renpy.org/latest.html

下载 SDK 7z.exe 与在 EditorDownload 选项中的 Atom 编辑器后我们即可开发 下载后打开 RenPy 就可以进入我们的游戏引擎里面了

RenPy 的 HelloWorld

点击 创建新工程 可以创建工程输入工程名的时候记得只能输入非中文的 ASCII 字符 在选择工程 DPI 的时候最好选择 1280x720, 创建完毕后我们就可以在工程文件内看到自己的工程了 点击工程中自己的工程可以打开工程 打开工程之后点击 编辑文件栏中的 Script.rpy​ 就可以进行编辑了

label start:
    # 这个StattLabel表示开始
  
    "游戏开始!"

    return

角色对白

在打开目录中有一个重要的文件夹就是 image 文件夹 里面是存放所有图片的目录 图片需要是 png 图片 否则不能显示 且文件名只能是 png 图片 因为 PNG 图片才支持透明色

# region 剧本筹备

define e = Character("新角色")
# 定义一个角色 他的名字叫做新角色

# endregion
label start:
    show image_caocao

Tornado 百万并发网站开发入门(未完善)

学习之前我们需要更好的了解异步的概念 http://note.youdao.com/noteshare?id=f965401a8ce9c64f2eb3a1461b1e2360&sub=7AD7CE3D175E4BAF91B528C975B71660 是我之前的笔记 各位可以赏光

Tornado 文档: https://tornado-zh.readthedocs.io/zh/latest/guide/intro.html

介绍

组成

  • Web 框架 创建 app 的 RequestHandler​类与更多的支持类
  • HTTP 的客户端 AsyncHTTPClient​与服务端实现 HttpServer
  • 异步网络库 IOLoop 与 IOStream
  • tornado.gen 协程库

优势与缺点

  • 适合 长轮询 WebSocket 大量连接 的应用
  • 微框架 高性能
  • 异步支持
  • 适合微服务
  • 缺点:插件少

运行

长链接与短链接

在这里插入图片描述

实时 web 功能需要给每个用户提供一个 长链接​,在传统的 web 开发中意味着需要给每个用户开辟一个内存进行线程的通信,导致服务器的开销昂贵

为了解决这个问题 Tornado 使用了单线程进行通信 也就是 类似 epoll 的单线程的事件循环 这就意味着所有的代码都是非 阻塞​的进行等待,用一个时间都是只有个一个操作是有效的

阻塞

一个函数在等待某些事情的返回值的时候会被 阻塞. 函数被阻塞的原因有很多: 网络 I/O,磁盘 I/O,互斥锁等.事实上 每个 函数在运行和使用 CPU 的时候都或多或少 会被阻塞(举个极端的例子来说明为什么对待 CPU 阻塞要和对待一般阻塞一样的严肃: 比如密码哈希函数 bcrypt, 需要消耗几百毫秒的 CPU 时间,这已 经远远超过了一般的网络或者磁盘请求时间了).

一个函数可以在某些方面阻塞在另外一些方面不阻塞.例如, tornado.httpclient​ 在默认的配置下,会在 DNS 解析上面阻塞,但是在其他网络请 求的时候不阻塞 (为了减轻这种影响,可以用 ThreadedResolver​ 或者是 通过正确配置 libcurl​ 用 tornado.curl_httpclient​ 来做). 在 Tornado 的上下文中,我们一般讨论网络 I/O 上下文的阻塞,尽管各种阻塞已经被最小 化.

异步

异步 函数在会在完成之前返回,在应用中触发下一个动作之前通常会在后 台执行一些工作(和正常的 同步 函数在返回前就执行完所有的事情不同).这里列 举了几种风格的异步接口:

  • 回调参数
  • 返回一个占位符 (Future​, Promise​, Deferred​)
  • 传送给一个队列
  • 回调注册表 (POSIX 信号)

不论使用哪种类型的接口, 按照定义 异步函数与它们的调用者都有着不同的交互方 式;也没有什么对调用者透明的方式使得同步函数异步(类似 gevent 使用轻量级线程的系统性能虽然堪比异步系统,但它们并 没有真正的让事情异步).

安装

pip install tornado

在这里插入图片描述

HelloWorld 的演示

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("HelloWorld")

def mk_app():
    return tornado.web.Application([
        (r'/',MainHandler), # 用户在访问根目录的时候 使用MainHandler进行处理
    ])

if __name__ == '__main__':
    app = mk_app() # 获取APP
    app.listen(8888) # APP 在 8888 端口进行访问监听
    tornado.ioloop.IOLoop.current().start()

如何关闭暂用的端口 以下是 CMD 命令

netstat -aon|findstr "8888"

taskkill -f -pid 32880

主要的模块

Tornado.web Application 和 RequestHandler 类处理 http 请求

tornado.template 模板的渲染

tornado.routing 处理的路由

模板渲染

与 Falsk 一致 我们需要创建一个目录进行存放网站的模板, 文件名称随意

import tornado.ioloop
import tornado.web
import os


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")  # 使用 render 渲染模板


def mk_app():
    return tornado.web.Application([
        (r'/index', MainHandler),
    ],
        template_path=os.path.join(
            os.path.dirname(__file__), "Templates" # 这里的字符串是设置项目模板的目录名
        ),
        debug=True  # 开启DEBUG模式
    )


if __name__ == '__main__':
    app = mk_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

给模板传递变量

用户可以在 render 中使用参数传值的方法给网站的模板传值

render("xxx.html",user="Undefind")

模板的编写就是:

<body>
	This is template File of tornado Now User:{{User}}
</body>

前往文档可以查看模板的语法: https://tornado-zh.readthedocs.io/zh/latest/guide/templates.html

异步并发

为了简便复制代码这一张我修改的是我们写的 Handler 句柄对象的 get 方法 其他的代码暂时忽略(其他的代码与之前模板章的一致)

  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    545 引用 • 672 回帖
1 操作
KuMa 在 2023-06-04 20:22:07 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...