快乐学习
前程无忧、中华英才非你莫属!

python-pip包管理器详解-2

zhangtongle阅读(2367)

优化1:使用国内镜像仓库

当你使用pip 安装的时候,会经常出现慢,timeout异常,这个时候咋解决,别怕非常简单:

pip install requests==2.6.0 -i https://pypi.douban.com/simple/

这里指定国内的模块仓库地址就可以解决了,这个地址我们网友叫豆瓣源。
如果初选timeout 异常。你就看一大堆的信息里面,有没有timeout关键字,如果有就在后面通过 -i 选项后面接着豆瓣源的地址,就成。

优化2:让原有的模块升级到新版本

如果你的机器上安装了requests,你在次执行安装命令没有任何效果
为了能使用上最新版本的requests, 直接添加--upgrade 升级选项。

python -m pip install --upgrade requests

优化3:条件限定安装模块的版本

如果有这样的需求,安装requests模块不能低于某个版本。或者指定的版本在仓库中不见了,该如何是好,有解决办法

python -m pip install "requests>=2.6.0"  # minimum version

当使用比较运算符例如 >, < 或其他某些可以被终端所解析的特殊字符时,包名称与版本号应当用双引号括起来。

优化4:无权时需指定用户安装模块

如果你的服务器不是自己一个人在用,有很多用户再用。并且有严格权限控制。需要把python模块安装到某个用户的下面。并且不需要root权限就能安装模块。

pip install --user poetry

# 会自动安装到C:\Users\Administrator\AppData\Roaming\Python\Python37\Scripts
#linux 通常是 ~/.local/
# Windows 用户的 %APPDATA%\Python

# 你还可以指定用户的基础目录
set PYTHONUSERBASE=c:/myappenv
pythohn -m pip install --user SomePackage

pip 异常: ensurepip

一般linux 默认是不安装pip 的,或者windows 上升级pip,以及环境变量配置问题导致,使用不了pip,解决方式

python -m ensurepip
python -m pip install --upgrade pip

优化5:本地安装第三方模块

利用镜像和缓存,快速进行本地第三方模块安装,

1、在能联网的电脑上,下载你需要的二进制安装包

pip download -r requirements.txt -d "./package" -i https://pypi.douban.com/simple/

2、在本地不能联网的机器上安装离线的二进制安装包

切换到虚拟环境,然后再进行安转:

(ztloo) D:\rd\PyJob\AutoTest\lib>pip install --no-index --find-links=./package -r requirements.txt

注明:不通过联网,即可安装。 还有搭建镜像服务、二进制web服务,这里不过多介绍。

优化6:安装被自己修改过的源码包

源码包一定要去https://pypi.org/ 去寻找对应模块的二进制包

不要去github上作者上传的,因为不知道它用什么打包的,按照他的安装方式,是不行的。
so, 他能把包传到中央仓库,说明那个才是最规范的。

下载好源码包,用解压软件解压,我推荐7-zip 解压缩软件,

解压好之后,把源码中某些文件或代码,替换成你自定义的,你要保证代码没有错误哈,
然后在用用7-ZIP压缩两次即可。
第一次压缩成tar格式,第二次再将第一次的压缩的.tar文件再次用7-ZIP压缩成gzip格式,最终得到的文件就是.tar.gz格式文件。 然后再用pip 指定压缩好的文件,进行安装即可。

pip install  pytest-html-3.1.1.tar.gz

优化7:直接使用别人编译好的whl

如果你是window 用户,我推荐你这么干。
https://www.lfd.uci.edu/~gohlke/pythonlibs/

这个网站里面,全都是编译好的windows版本的离线文件,很多,通过ctrl+F 关键字很快就能找到我们想要的包。安装起来也很简单

pip install pandas‑1.4.3‑pp38‑pypy38_pp73‑win_amd64.whl

pandas‑1.4.3‑pp38‑pypy38_pp73‑win_amd64.whl 这个就是我们从这个网站上找到下载的的padnas 的离线安装包。 把它放到你所知道的目录,然后再用pip install 进行安装。

pandas‑1.4.3: pandas 的版本。
pp38‑pypy38_pp73: 应该是支持python3.8
win_amd64: window 平台 amd64位架构的

以上,都是小编,亲测,没有太大的问题,如果遇到问题,可以呼叫小编,如果他没理你,有可能他在睡觉,你尝试用红包叫醒它。

如果你连pip 还是不肾了解:那请看看上一篇文章,pip包管理器详解-1

python-pip包管理器详解-1

zhangtongle阅读(2123)

pip 是python 中的包管理器

我们在使用python第三方模块(有些语言叫包),其实叫包更为贴切些,
大家都知道在python 安装第三方模块,其实很简单:

pip install jieba

pip工具就会自动通过联网 到https://pypi.org 这个地址找到你要安装的jieab,进行下载安装到默认的位置。 你还可以指定下载哪个版本

pip install jieba==0.41

为啥要指定版本,直截了当的告诉你,因为新版本的jieba使用python 最新版编译的,你目前的python为老版本,多多少少会出现异常,你只能被逼无奈降低jieba 的版本。这就是为啥我们要指定版本。

这里值得注意的是,pip包管理器最常用的三个子命令


install: 安装模块 
uninstall : 卸载模块
freeze:以需求格式输出已安装的包

还有值得注意的是,一旦你的电脑中安装了多个python环境,例如你同时安装了python3.5,3,6 ,3,8 然后你又不知道当前的环境变量是哪个python版本。如果贸然执行pip install,就会不清楚安装到哪个版本上了。

有的时候还会出现pip 命令不是系统命令。比较稳妥的做法,比如你你只想给python3.8 版本安装requests模块,你应该通过cmd 控制台,切换到python3.8 的安装目录,在执行python -m 来安装。

cd D:\rd\py38\
python -m pip install requests==2.6.0

显示包的信息

pip show requests
D:\rd\py38>pip show requests
Name: requests
Version: 2.26.0
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: d:\rd\py38\lib\site-packages
Requires: certifi, charset-normalizer, idna, urllib3
Required-by: DingtalkChatbot, oss2, premailer, pytest-base-url, pytest-selenium,
 yarg

显示所有模块

D:\rd\py38>pip list
Package                           Version
--------------------------------- ----------
aliyun-python-sdk-core            2.13.36
aliyun-python-sdk-kms             2.15.0
allure-pytest                     2.9.45
allure-python-commons             2.9.45
Appium-Python-Client              1.3.0
argcomplete                       2.0.0
asgiref                           3.3.4
click                             7.1.2
colorama                          0.4.5

把所有模块导入文件中

pip freeze > requirements.txt

从文件中安装所有模块

python -m pip install -r requirements.txt

python-mitmproxy-交互式-https-代理-2

zhangtongle阅读(4350)

使用代理

为啥要使用代理,是因为我们谷歌浏览器可以通过插件配置好代理,我们通过谷歌浏览器访问的所有网站请求,都会经过mitmproxy,但是我们写的脚本和shell 不会,所以需要告诉大家 如何在程序上加代理。我们程序访问的所有网站urL和接口都会经过mitmproxy-代理服务器,这样才能进行保存我们的所有记录,过滤,统计等额外操作。

curl --proxy http://127.0.0.1:8080 "http://wttr.in/Innsbruck?0" 

UI自动化加代理

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://127.0.0.1:8080")

#你如果代理服务器多

proxy_arr = [
     '--proxy-server=http://60.168.81.177:1133',
     '--proxy-server=http://118.212.107.177:9999',
     '--proxy-server=http://36.248.132.145:9999',
     '--proxy-server=http://163.125.112.207:8118',
     '--proxy-server=http://119.134.110.69:8118',
     '--proxy-server=http://123.163.27.101:9999',
     '--proxy-server=http://1.198.73.167:9999'
 ]

proxy = random.choice(proxy_arr)  # 随机选择一个代理
chrome_options.add_argument('--proxy-server='+proxy)
browser = webdriver.Chrome(options=chrome_options)

Requests模块代理:接口自动化加代理

import requests

proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}

requests.get('http://example.org', proxies=proxies)

插件

Mitmproxy 的插件机制由一组支持任何复杂性组件的 API 组成。插件通过响应事件与 mitmproxy 交互,这允许它们连接并更改 mitmproxy 的行为

如果你想要实现,抓取特定的接口、内容、保存到你想要保存的地方。

你要使用mitmproxy,给我们的提供的插件功能,这个插件就是一个py程序

在这个py程序中,你可以定义好抓取内容,存储到哪的逻辑代码。 在开启mitmproxy 的同时指定脚本一起运行。

控制台执行命令:

mitmdump -s ./proxy_util.py

建议把mitmdump 配置到环境变量里面,要不然每次启动还得切换到mitmdump的安装目录甚是麻烦。

proxy_util.py 脚本长这样:

"""
Basic skeleton of a mitmproxy addon.

Run as follows: mitmproxy -s proxy_util.py
"""
from mitmproxy import ctx

class Counter:
    def __init__(self):
        self.num = 0

    def request(self, flow):
        self.num = self.num + 1
        ctx.log.info("We've seen %d flows" % self.num)

addons = [
    Counter()
]

"""

关于上面的代码,有几点需要注意:

Mitmproxy 获取addons全局列表的内容并将其找到的内容加载到插件机制中。
插件只是对象——在这种情况下,我们的插件是Counter.
该request方法是一个事件的例子。插件只是为他们想要处理的每个事件实现一个方法。
每个事件都有一个由传递给方法的参数组成的签名。因为request,这是 的一个实例mitmproxy.http.HTTPFlow。
最后,该ctx模块是一个holdall 模块,它公开了一组在插件中常用的标准对象。我们可以将一个ctx对象作为第一个参数传递给每个事件,但我们发现将其公开为可导入的全局对象更为简洁。在这种情况下,我们使用该ctx.log对象来进行日志记录。

"""

插件之给response添加header


"""Add an HTTP header to each response."""

class AddHeader:
    def __init__(self):
        self.num = 0

    def response(self, flow):
        self.num = self.num + 1
        flow.response.headers["count"] = str(self.num)

addons = [
    AddHeader()
]

支持的事件

这列只说明我们用到的HTTP事件,*(还包括通用事件和webscocket事件,不过我们自动化项目暂时用不到),如果需要请看官方文档,下面给出具体链接:
https://docs.mitmproxy.org/archive/v5/addons-events/

上面插件里已经包含了了两个事件

request 客户端请求事件、response服务端返回数据事件

我们用这些事件,就可以轻松拦截用户的请求,获取服务端返回的数据篡改、保存到我们想要保存的地方一些列操作,操控性,可玩性大大增加。其实核心的我们知道request和response 怎么用就行了。

具体所有1HTTP事件如下:

"""HTTP-specific events."""
import mitmproxy.http

class Events:
    def http_connect(self, flow: mitmproxy.http.HTTPFlow):
        """
            An HTTP CONNECT request was received. Setting a non 2xx response on
            the flow will return the response to the client abort the
            connection. CONNECT requests and responses do not generate the usual
            HTTP handler events. CONNECT requests are only valid in regular and
            upstream proxy modes.
        """

    def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
        """
            HTTP request headers were successfully read. At this point, the body
            is empty.
        """

    def request(self, flow: mitmproxy.http.HTTPFlow):
        """
            The full HTTP request has been read.
        """

    def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
        """
            HTTP response headers were successfully read. At this point, the body
            is empty.
        """

    def response(self, flow: mitmproxy.http.HTTPFlow):
        """
            The full HTTP response has been read.
        """

    def error(self, flow: mitmproxy.http.HTTPFlow):
        """
            An HTTP error has occurred, e.g. invalid server responses, or
            interrupted connections. This is distinct from a valid server HTTP
            error response, which is simply a response with an HTTP error code.
        """

有了这些事件,我们就可以做很多事情了,比如可以获取到用户与服务端链接状态。获取请求的header,获取到用户请求失败的状态码啊,等等。

脚本

脚本是插件的进阶,之前的插件,都需要创建个类,还得再结尾添加
addons = [Counter()],非常的麻烦,为了简化这一操作,mitmproxy 有个脚本的概念。

有时,我们想编写一个快速的脚本,而不用经历创建类的麻烦。插件机制有一个简写,它允许将一个模块作为一个整体视为一个插件对象。这让我们可以在模块范围内放置事件处理函数。例如,这是一个完整的脚本,它为每个请求添加了一个标头。

案例:截获对特定 URL 的请求并发送任意响应的示例

"""Send a reply from the proxy without sending any data to the remote server."""
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    if flow.request.pretty_url == "http://example.com/path":
        flow.response = http.HTTPResponse.make(
            200,  # (optional) status code
            b"Hello World",  # (optional) content
            {"Content-Type": "text/html"}  # (optional) headers
        )

API

官方文档他没有提供API脚本中flow对象包含那些属性和方法的,原文说浪漫我们自己参考源码自己看就OK了。

官方的原话是酱紫:

本文档将向您展示如何使用事件、选项 和命令构建插件。但是,这不是 API 手册,mitmproxy 源代码仍然是规范参考。从命令行探索 API 的一种简单方法是使用pydoc。例如,这里是一个显示 mitmproxy 的 HTTP 流类的 API 文档的命令:

好吧,我们使用

python -m pydoc mitmproxy.http

好了,官方文档也比较简单,也就这么多。

备注

这个还是比较重点,有的小伙伴不会编程,工作中也需要经常抓包改包,fiddler、charles 也是能够满足,如果你想要非常的灵活的抓包,假设服务端抓包、架设服务端抓包功能,一定要在linux 安装mitmproxy ,因为windows不支持在cmd窗口中,进行命令式交互,只有linux 支持。

但是因为我们的项目是需要全脚本,无手工参与的自动化过程,所以可以安装在windows。

python-mitmproxy-交互式 HTTPS 代理-1

zhangtongle阅读(2579)

简介

最近要实现接口自动化测试的,流量录制和回放。

mitmproxy最为合适,基于服务器的,基于Python的,可编程的。可互动的。so,已经抛弃了什么Fiddler 、charles ,因为它才是最好的。

他的背景是,适用于渗透测试人员和软件开发人员的必备利器,拿过来做接口自动化测试的流量录制和回放,可能也就不到3行代码就能实现。so ,我选择它来做我们的工具组件之一。

安装

安装包下载地址:
https://mitmproxy.org/downloads/#5.3.0/

你什么系统就下什么安装包,windows 版本,直接点下一步下一步,点击击默认安装就好。

千万别下最新版本,因为它跟python版本强依赖。最新版本8.x 需要python3.10x版本,我的py是3.8 只能安装5.x版本。

其他版本的安装你不会,可以看这个文档
https://docs.mitmproxy.org/archive/v5/overview-installation/

启动

windows版本安装完成会自动启动

如果没有启动可以手动启动,或者下次启动可以按照我说的:

如果你的mitmproxy安装在默认路径下。可以打开cmd 命令窗口
切换到图片中所在的路径执行:mitmdump 并回车,就可以开启。

监听端口8080,配置谷歌浏览器代理。

在谷歌浏览器上,安转一个插件:SwitchyOmega_Chromium.zip

可以找博客主要,或者自己百度下载,然后顺手百度一下,谷歌浏览器如何安装插件即可。

安装好之后,配置mitmproxy服务器的监听端口号为8080

然后在配置好的代理环境下访问 https://www.ztloo.com

可以看到访问https://www.ztloo.com 可以成功,显示200,其他链接

Cannot establish TLS with client (sni: beacons.gvt2.com): TlsException("SSL) 会出现这样的错误,是因为需要安装证书,它跟fiddler、charles 一样也是需要安装证书的,要不然抓不了HTTS的数据包。

安装证书

http://mitm.it/ 在谷歌浏览器代理环境下,访问这个链接,会提示你安装对应的证书引导提示

点击对应的get绿色区域,会自动下载证书文件,然后安装即可。

比如windows 上安装双击下列文件(跟着提示,安装到根证书即可)
不需要设置密码

mitmproxy-ca-cert.p12

然后就能抓取https 的数据包了,例如百度。

手机上安装

访问http://mitm.it/ 下载对应手机的证书,发送到对应手机,
如果手机不识别,就去安卓手机的安全、从SD卡下安装证书。

手机端iphone上下载安装mitmproxy证书:

  • 手机和PC在同一个局域网中,设置wifi代理为PC端的ip,端口为mitmproxy的端口(默认8080)

  • 手机浏览器访问mitm.it,下载安装mitmproxy描述文件,完成验证

  • 经过上面两步,个别APP就可以访问并被mitmproxy在PC端截获,但有时发现很多APP无法上网,这时还需要添加证书的信任

“设置”——关于本机——证书信任设置——开启mitmproxy完全信任

安卓端android(华为为例)安卓端android(华为为例)

  • 1 设置

  • 2 安全与隐私

  • 3 更多安全设置

  • 4 从存储设备安装

  • 5 选中证书文件,点击安装

  • 6 输入锁屏密码

  • 7 给安装文件命名mitmproxy


更详细的说明可以参考:https://mitmproxy.org/,后续会更新更为实战,更为好玩的数据抓取功能。一定要持续关注哦~

Python排序指南-(社区文档)

zhangtongle阅读(2485)

前言

Python 列表有一个内置的 list.sort() 方法可以直接修改列表。
还有一个 sorted() 内置函数,它会从一个可迭代对象构建一个新的排序列表。

在本文档中,我们将探索使用Python对数据进行排序的各种技术。

基本排序

简单的升序排序非常简单:只需调用 sorted() 函数。它返回一个新的排序后列表:

sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

你也可以使用 list.sort() 方法,它会直接修改原列表(并返回 None 以避免混淆),通常来说它不如 sorted() 方便 但如果你不需要原列表,它会更有效率。

a = [5, 2, 3, 1, 4]
a.sort()
a
[1, 2, 3, 4, 5]

另外一个区别是, list.sort() 方法只是为列表定义的,而 sorted() 函数可以接受任何可迭代对象。

sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]

关键函数

list.sort() 和 sorted() 都有一个 key 形参用来指定在进行比较前要在每个列表元素上调用的函数(或其他可调用对象)。
例如,下面是一个不区分大小写的字符串比较:

sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']

key 形参的值应该是个函数(或其他可调用对象),它接受一个参数并返回一个用于排序的键。 这种机制速度很快,因为对于每个输入记录只会调用一次键函数。

一种常见的模式是使用对象的一些索引作为键对复杂对象进行排序。例如:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]
sorted(student_tuples, key=lambda student: student[2])   # sort by age
('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

同样的技术也适用于具有命名属性的对象。例如:

class Student:
    def __init__(self, name, grade, age):
        self.name = name
        self.grade = grade
        self.age = age
    def __repr__(self):
        return repr((self.name, self.grade, self.age))

student_objects = [
    Student('john', 'A', 15),
    Student('jane', 'B', 12),
    Student('dave', 'B', 10),
]
sorted(student_objects, key=lambda student: student.age)   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Operator 模块函数

上面显示的键函数模式非常常见,因此 Python 提供了便利功能,使访问器功能更容易,更快捷。 operator 模块有 itemgetter() 、 attrgetter() 和 methodcaller() 函数。

使用这些函数,上述示例变得更简单,更快捷:

from operator import itemgetter, attrgetter

sorted(student_tuples, key=itemgetter(2))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

sorted(student_objects, key=attrgetter('age'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Operator 模块功能允许多级排序。 例如,按 grade 排序,然后按 age 排序:

sorted(student_tuples, key=itemgetter(1,2))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]

sorted(student_objects, key=attrgetter('grade', 'age'))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]

升序和降序

list.sort() 和 sorted() 接受布尔值的 reverse 参数。这用于标记降序排序。
例如,要以反向 age 顺序获取学生数据:


sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)

sorted(student_objects, key=attrgetter('age'), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]

排序稳定性和排序复杂度

排序保证是 稳定 的。 这意味着当多个记录具有相同的键值时,将保留其原始顺序。

data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]

注意 blue 的两个记录如何保留它们的原始顺序,以便 ('blue', 1) 保证在 ('blue', 2) 之前。

这个美妙的属性允许你在一系列排序步骤中构建复杂的排序。
例如,要按 grade 降序然后 age 升序对学生数据进行排序,请先 age 排序,然后再使用 grade 排序:

s = sorted(student_objects, key=attrgetter('age'))     # sort on secondary key
sorted(s, key=attrgetter('grade'), reverse=True)       # now sort on primary key, descending
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

这可以被抽象为一个包装函数,该函数能接受一个列表以及字段和顺序的元组,以对它们进行多重排序。

def multisort(xs, specs):
    for key, reverse in reversed(specs):
        xs.sort(key=attrgetter(key), reverse=reverse)
    return xs

multisort(list(student_objects), (('grade', True), ('age', False)))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Python 中使用的 Timsort 算法可以有效地进行多种排序,因为它可以利用数据集中已存在的任何排序。


使用装饰-排序-去装饰的旧方法

这个三个步骤被称为 Decorate-Sort-Undecorate :

首先,初始列表使用控制排序顺序的新值进行修饰。

然后,装饰列表已排序。

最后,删除装饰,创建一个仅包含新排序中初始值的列表。

例如,要使用DSU方法按 grade 对学生数据进行排序:

decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]
decorated.sort()
[student for grade, i, student in decorated]               # undecorate
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]

这方法语有效是因为元组按字典顺序进行比较,先比较第一项;如果它们相同则比较第二个项目,依此类推。

不一定在所有情况下都要在装饰列表中包含索引 i ,但包含它有两个好处:

排序是稳定的——如果两个项具有相同的键,它们的顺序将保留在排序列表中。

原始项目不必具有可比性,因为装饰元组的排序最多由前两项决定。 因此,例如原始列表可能包含无法直接排序的复数。

这个方法的另一个名字是 Randal L. Schwartz 在 Perl 程序员中推广的 Schwartzian transform。

既然 Python 排序提供了键函数,那么通常不需要这种技术。


使用 cmp 参数的旧方法

本 HOWTO 中给出的许多结构都假定为 Python 2.4 或更高版本。在此之前,没有内置 sorted() , list.sort() 也没有关键字参数。相反,所有 Py2.x 版本都支持 cmp 参数来处理用户指定的比较函数。

在 Py3.0 中, cmp 参数被完全删除(作为简化和统一语言努力的一部分,消除了丰富的比较与 cmp() 魔术方法之间的冲突)。

在 Py2.x 中, sort 允许一个可选函数,可以调用它来进行比较。
该函数应该采用两个参数进行比较,然后返回负值为小于,如果它们相等则返回零,或者返回大于的正值。
例如,我们可以这样做:

def numeric_compare(x, y):
    return x - y  # 返回负值为小于,小的值放在前面
sorted([5, 2, 4, 1, 3], cmp=numeric_compare) 
[1, 2, 3, 4, 5]

或者你可反转比较的顺序:


def reverse_numeric(x, y):
    return y - x # 返回正值,大的值放在前面
sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) 
[5, 4, 3, 2, 1]

在将代码从 Python 2.x 移植到 3.x 时,如果让用户提供比较函数并且需要将其转换为键函数则会出现这种情况。 以下包装器使得做到这点变得很容易。

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K:
        def __init__(self, obj, *args):
            self.obj = obj
        def __lt__(self, other):
            return mycmp(self.obj, other.obj) < 0
        def __gt__(self, other):
            return mycmp(self.obj, other.obj) > 0
        def __eq__(self, other):
            return mycmp(self.obj, other.obj) == 0
        def __le__(self, other):
            return mycmp(self.obj, other.obj) <= 0
        def __ge__(self, other):
            return mycmp(self.obj, other.obj) >= 0
        def __ne__(self, other):
            return mycmp(self.obj, other.obj) != 0
    return K

要转换为键函数,只需包装旧的比较函数:

sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))

在 Python 3.2 中, functools.cmp_to_key() 函数被添加到标准库中的 functools 模块中

原创-Pandas心法之数据字符串与正则处理-8

zhangtongle阅读(2364)

"""
字符串操作
"""
import pandas as pd

monte = pd.Series(['Graham Chapman', 'John Cleese', 'Terry Gilliam',
                   'Eric Idle', 'Terry Jones', 'Michael Palin'])
print(monte)
print(monte.str.lower())
print(monte.str.len())
print(monte.str.startswith('T'))
print(monte.str.split())

'''
字符串函数
len() lower() translate() islower()
ljust() upper() startswith() isupper()
rjust() find() endswith() isnumeric()
center() rfind() isalnum() isdecimal()
zfill() index() isalpha() split()
strip() rindex() isdigit() rsplit()
rstrip() capitalize() isspace() partition()
lstrip() swapcase() istitle() rpartition()

'''

'''
使用正则表达式
'''
'''

Method Description
match() Call re.match() on each element, returning a boolean.
extract() Call re.match() on each element, returning matched groups as strings.
findall() Call re.findall() on each element
replace() Replace occurrences of pattern with some other string
contains() Call re.search() on each element, returning a boolean
count() Count occurrences of pattern
split() Equivalent to str.split(), but accepts regexps
rsplit() Equivalent to str.rsplit(), but accepts regexps

'''

# 通过在每个元素的开头要求一组连续的字符来从每个名字中提取名字:
# print(monte.str.extract('([A-Za-z]+)', expand=False))

'''
例如查找所有以辅音开头和结尾的名称,
并使用字符串开头(^)和字符串结尾($)正则表达式字符
'''
print(monte.str.findall(r'^[^AEIOU].*[^aeiou]$'))
print(monte.str[0:3])

'''
字符串还有一些有用的方法

get() 索引每个元素
slice() 切片每个元素
slice_replace() 用传递的值替换每个元素中的切片
cat() 连接字符串
repeat() 重复值
normalize() 返回字符串的Unicode形式
pad() 在字符串的左侧,右侧或两侧添加空格
wrap() 将长字符串拆分成长度小于给定宽度的行
join() 使用传递的分隔符将字符串连接到系列的每个元素中
get_dummies() 提取虚拟变量作为数据框

'''
print(monte.str.split().str.get(-1))

# 通过分隔符来查询
full_monte = pd.DataFrame({'name': monte,
                           'info': ['B|C|D', 'B|D', 'A|C',
                                    'B|D', 'B|C', 'B|C|D']})
print(full_monte)

# 该get_dummies()例程可让您快速将这些指标变量拆分为DataFrame:
print(full_monte['info'].str.get_dummies('|'))

原创-Pandas心法之数据透视表-7

zhangtongle阅读(2430)

###############透视表##########################
import pandas as pd
import seaborn as sns

# 科学上网就好了。
# 其中包含有关该不幸航程的每位乘客的大量信息,
# 包括性别,年龄,舱位,已付车费等。
titanic = sns.load_dataset('titanic')
# print(titanic.head())

# 让我们按性别查看存活率:
# print(titanic.groupby('sex')[['survived']].mean())

'''
这立即为我们提供了一些见识:
总体而言,船上每四位女性中就有三位得以幸存,而只有五分之一的男性得以幸存!
'''

'''
这很有用,但我们可能想更进一步,按照性别和阶级来考察生存率
'''

# print(titanic.head())
# print('--------------------------')

life = titanic.groupby(['sex', 'class'])['survived'].aggregate('mean').unstack()
print(life)

print('--------------------------')

'''
透视表

透视表默认处理函数是均值
'''
tsb= titanic.pivot_table('survived', index='sex', columns='class')
print(tsb)
'''
这显然比该groupby方法更具可读性,并且产生相同的结果。
您可能会期望20世纪初的跨大西洋航行,
生存梯度对女性和更高阶层的人都有利。
一流的妇女几乎可以确定地生存(嗨,罗斯!),而十分之三的男性中只有一个幸存了(抱歉,杰克!)。

'''

# 多级透视表
'''
例如,我们可能有兴趣将年龄视为第三维。
我们将使用以下pd.cut功能对年龄进行分类:
'''
age = pd.cut(titanic['age'], [0, 18, 80])

tsc = titanic.pivot_table('survived', ['sex', age], 'class')
# print(tsc)

'''
在处理列时,我们也可以应用相同的策略。
让我们添加pd.qcut用于自动计算分位数的票价信息:
'''
fare = pd.qcut(titanic['fare'], 2)
dfs = titanic.pivot_table('survived', ['sex', age], [fare, 'class'])
# print(dfs)

'''
额外的数据透视表选项
'''

# survived 统计它sum, fare 统计它均值,以class 分组。索引是性别
tps = titanic.pivot_table(index='sex', columns='class',
                          aggfunc={'survived': sum, 'fare': 'mean'})
# print(tps)

'''
有时,计算每个分组的总数很有用。这可以通过margins关键字完成:
'''
tcm = titanic.pivot_table('survived', index='sex', columns='class', margins=True)

# print(tcm)
'''
在这里,这自动为我们提供了有关按性别分类不可知生存率,
按类别分类不可知性别生存率以及总生存率38%的信息。
可以使用margins_name关键字指定边距标签,默认为"All"。
'''

'''
例如:生育水平数据
'''
births = pd.read_csv('births.csv')
# print(births.head())

'''
通过使用数据透视表,我们可以开始更多地了解此数据。
让我们添加一个十年专栏,看看男女出生与十年的关系:
'''
births['decade'] = 10 * (births['year'] // 10)
bgs = births.pivot_table('births', index='decade', columns='gender', aggfunc='sum')
# print(bgs)

原创-Pandas心法之数据汇总与分组-6

zhangtongle阅读(2287)

###############汇总与分组##########################
import seaborn as sns
import numpy as np
import pandas as pd

planets = sns.load_dataset('planets')
planets.shape

# print(planets.head())

rng = np.random.RandomState(42)
ser = pd.Series(rng.rand(5))
ser.sum()
ser.mean()

df = pd.DataFrame(
    {'A': rng.rand(5), 'B': rng.rand(5)}
)

print(df.mean())
print(df.mean(axis='columns'))

print(planets.dropna().describe())

'''
Aggregation Description
count() Total number of items
first(), last() First and last item
mean(), median() Mean and median
min(), max() Minimum and maximum
std(), var() Standard deviation and variance
mad() Mean absolute deviation
prod() Product of all items
sum() Sum of all items
These are all methods of DataFrame and Series objects.
'''

# GroupBy: Split, Apply, Combine

df = pd.DataFrame({'key': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'data': range(6)}, columns=['key', 'data'])

print(df)
print(df.groupby('key').sum())

group_op = planets.groupby('method')['orbital_period']
print(group_op.median())

# 可以直接迭代每个组
for (method, group) in planets.groupby('method'):
    print("{0:30s} shape={1}".format(method, group.shape))

gdu = planets.groupby('method')['year'].describe().unstack()
print(gdu)
print(df)

'''
aggregate()方法允许更大的灵活性。
它可以采用字符串,函数或其列表,然后一次计算所有聚合
'''
dfk = df.groupby('key').aggregate(['min', np.median, max])
print(dfk)

rng = np.random.RandomState(0)
df = pd.DataFrame(
    {'key': ['A', 'B', 'C', 'A', 'B', 'C'],
     'data1': range(6),
     'data2': rng.randint(0, 10, 6)},
    columns=['key', 'data1', 'data2'])

dfagg = df.groupby('key').aggregate({'data1': 'min',
                                     'data2': 'max'})
print(dfagg)

# 过滤
def filter_func(x):
    return x['data2'].std() > 4

print(df)

print('-----------------------------')
print(df.groupby('key').std())
print('-----------------------------')
print(df.groupby('key').filter(filter_func))

print('-----------------------------')
dfr = df.groupby('key').transform(lambda x: x - x.mean())
print(dfr)

# apply() 方法的应用

'''
该apply()方法使您可以将任意函数应用于分组结果。
该函数应采用DataFrame,并返回Pandas对象(
例如DataFrame,Series)或标量;合并操作将根据返回的输出类型进行调整。
'''

def norm_by_data2(x):
    # x is a DataFrame of group values
    x['data1'] /= x['data2'].sum()
    return x

df_norm = df.groupby('key').apply(norm_by_data2)
print(df_norm)

# 指定分离键
L = [0, 1, 0, 1, 2, 0]
print(df.groupby(L).sum())

# 字典或series 映射索引到组
df2 = df.set_index('key')
print(df2)

mapping = {'A': 'vowel', 'B': 'consonant', 'C': 'consonant'}
print('------------------------------')
print(df2.groupby(mapping).sum())

# 与映射类似,您可以传递将输入索引值并输出组的任何Python函数
print(df2.groupby(str.lower).mean())
print(df2.groupby([str.lower, mapping]).mean())

# 分组案例
decade = 10 * (planets['year'] // 10)
decade = decade.astype(str) + 's'
decade.name = 'decade'
print(planets.groupby(['method', decade])['number'].sum().unstack().fillna(0))

原创-Pandas心法之数据连接&合并-5

zhangtongle阅读(2354)

前言

Pandas 下半部分内容,来了。这些示例整合了一下,直接贴上,没怎么分类,因为比较懒

但是内容,肯定是很精华,并配上了中文的注释,并且每行代码,都经过小编的运行和验证。含金量还是非常之高。

示例代码的主要内容有、数据的合并concat、merge 这两个函数,如果你有大量的Excel, 数据库表的复杂链接查询的时候,发现SQL 很慢的时候,可以试试merge函数。

还有分组和汇总,当我们的数据有很多突出的特征,我们就可以进行分类统计和描述。
分类统计和描述,就用到了分组函数 gruopby,结合aggregate和聚合函数max,min等就可以了。

还有更为简洁高级点的分组统计功能,那就是透视表pivot_table

还有字符串处理函数,以及时间字段的处理


import numpy as np
import pandas as pd

# 将Series 和DataFrame与pd.concat函数连接在一起
from datetime import datetime

def make_df(cols, ind):
    """Quickly make a DataFrame"""
    data = {c: [str(c) + str(i) for i in ind]
            for c in cols}

    return pd.DataFrame(data, ind)

# example DataFrame
md = make_df('ABC', range(3))
# print(md)

# numpy数组的串联
x = [1, 2, 3]
y = [4, 5, 6]
z = [7, 8, 9]
xyz = np.concatenate([x, y, z])
# print(xyz)

# 设置轴来设定串联的方向
x = [[1, 2],
     [3, 4]]
xx = np.concatenate([x, x], axis=1)  # 1 是x轴方向
# print(xx)

# pd.concat()可以用于Series或DataFrame对象的简单串联
ser1 = pd.Series(['A', 'B', 'C'], index=[1, 2, 3])
ser2 = pd.Series(['D', 'E', 'F'], index=[4, 5, 6])
ser = pd.concat([ser1, ser2])
# print(ser)

df1 = make_df('AB', [1, 2])
df2 = make_df('AB', [3, 4])

df = pd.concat([df1, df2])
# print('这是concat的df',df)

df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])

dfs = pd.concat([df3, df4], axis=1)
# print(dfs)

# 重复索引
x = make_df('AB', [0, 1])
y = make_df('AB', [2, 3])
y.index = x.index  # make duplicate indices!

xys = pd.concat([x, y])
# print(xys)

# 给两个DF数据分别在左侧X轴,添加多层索引
xyk = pd.concat([x, y], keys=['x', 'y'])
# print(xyk)

# 串联部分索引不同,出现NaN
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [3, 4])
# 解决办法,使用join 函数内连接,把索引相同部分进行关联
# 其他不关联的索引A,D舍去。可以理解为sql 的内连接
dfn = pd.concat([df5, df6], join='inner')
# print(dfn)

# 可以理解为sql 的左链接,以df5 为基准。
dfc = pd.concat([df5, df6], join_axes=[df5.columns])
# print(dfc)

# 通过append 函数 来合并数据.效果一样
# print('这是append的df', df1.append(df2))

# 通过append方法不会修改原始对象,
# 而是会使用合并的数据创建一个新对象
# 还是使用concat 比较高效。、

# 1、 高性能合并Merge 合并多个数据源
df_emp = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue', 'ztloo'],
                       'group': ['Accounting', 'Engineering', 'Engineering', 'HR', 'code']})
df_date = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue', 'pys'],
                        'hire_date': [2004, 2008, 2012, 2014, 2020]})

# print(df_emp)
# print(df_date)

# 按照第一个参数df_emp 的索引顺序排列的
# merge 只合并两个数据源相同的列,不同的列会舍弃
df3 = pd.merge(df_emp, df_date)
# print(df3)

df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
                    'supervisor': ['Carly', 'Guido', 'Steve']})

# 两个df 在去合并一个df,通过group,新增一个关系列。
df34 = pd.merge(df3, df4)
# print(df34)

# 合并的df 数据中有重复索引
df5 = pd.DataFrame({'group': ['Accounting', 'Accounting',
                              'Engineering', 'Engineering', 'HR', 'HR'],
                    'skills': ['math', 'spreadsheets', 'coding', 'linux',
                               'spreadsheets', 'organization']})
print(df_emp)

# 根据df_emp 中的grop角色进行关联合并。
df15 = pd.merge(df_emp, df5)
print(df15)

# 通过关键字来合并,显示指定两个df 中都有的字段,用来作为关联。
dfe12 = pd.merge(df_emp, df_date, on='employee')
print(dfe12)

df3 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue', 'dssf'],
                    'salary': [70000, 80000, 120000, 90000, 1009009]})

# 两个df 中,关联两个索引名称不同的方法。
# 通过left_on和right_on 来指定左右关联的索引名称
df13_lr = pd.merge(df_emp, df3, left_on="employee", right_on="name")
print(df13_lr)

# 去掉重复的列,删掉name 列。
print(df13_lr.drop('name', axis=1))

df1a = df_emp.set_index('employee')
df2a = df_date.set_index('employee')
print(df1a)
print(df2a)

dfaa = pd.merge(df1a, df2a, left_index=True, right_index=True)
print(dfaa)

print(df1a.join(df2a))
print(df1a)

# 因为df1a 的索引set_index 被设置成了employee,so 直接关联name.
dfa3 = pd.merge(df1a, df3, left_index=True, right_on='name')
print(dfa3)

# 严重的问题
# 两个df 中有不同的列,使用merge 只会合并都存在的列
df6 = pd.DataFrame({'name': ['Peter', 'Paul', 'Mary'],
                    'food': ['fish', 'beans', 'bread']},
                   columns=['name', 'food'])

df7 = pd.DataFrame({'name': ['Mary', 'Joseph'],
                    'drink': ['wine', 'beer']},
                   columns=['name', 'drink'])

# 为了解决多个df 中不同的列,使用how=outer,
df67 = pd.merge(df6, df7,how='outer')
print(df67)

# 如果只显示第一个df数据中的所有列,left ,也可以使用right.
df67 = pd.merge(df6, df7, how='left')
print(df67)

# 合并两个df ,df 中有相同的列名
df8 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    'rank': [1, 2, 3, 4]})
df9 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    'rank': [3, 1, 4, 2]})

'''
由于输出将有两个冲突的列名,
因此合并功能会自动附加后缀_x或_y使输出列唯一。
如果这些默认值不合适,则可以使用suffixes关键字指定自定义后缀:
'''

df89 = pd.merge(df8, df9, on="name")
print(df89)

df89 = pd.merge(df8, df9, on="name", suffixes=["_L", "_R"])
print(df89)

##################################################################

pop = pd.read_csv('state-population.csv')
areas = pd.read_csv('state-areas.csv')
abbrevs = pd.read_csv('state-abbrevs.csv')

print('-----------pop------------')
print(pop.head())
#
print('-----------areas------------')
print(areas.head())

print('-----------abbrevs------------')
print(abbrevs.head())

# 让state/region和 abbreviation 字段相关联
merged = pd.merge(pop, abbrevs, how='outer',
                  left_on='state/region', right_on='abbreviation')

# drop duplicate info

# 删掉abbreviation 列
merged = merged.drop('abbreviation', axis=1)  # drop duplicate info
# print(merged.head())
#

# 检查数据是否有不匹配有空行
print(merged.isnull().any())

# 一些population信息为空;让我们找出:
pop_null = merged[merged['population'].isnull()].head()
# print(pop_null)

state_unique = merged.loc[merged['state'].isnull(), 'state/region'].unique()
# unique.返回Series对象的唯一值。
print(state_unique)

# 把合并好的数据中的state/region
# 等于pr的数据中的state 字段的值修改成Puerto Rico。
merged.loc[merged['state/region'] == 'PR', 'state'] = 'Puerto Rico'

# 把合并好的数据中的state/region 等于USA的数据中的state 字段的值修改成Puerto Rico

merged.loc[merged['state/region'] == 'USA', 'state'] = 'United States'

# any()一个True,则返回True,非空为False
print(merged.isnull().any())
print(merged.head())

# 将结果与面积数据合并。
# 检查我们的结果,我们将希望同时加入state这两个列:
final = pd.merge(merged, areas, on='state', how='left')
print(final.head())

print(final.isnull().any())

# area列中为空;我们可以看一下此处忽略了哪些区域:
fun = final['state'][final['area (sq. mi)'].isnull()].unique()
print(fun)

'''
我们看到,我们areas DataFrame不包括整个美国地区。
我们可以插入适当的值(例如,使用所有州面积的总和),
但是在这种情况下,我们将删除空值,因为整个美国的人口密度与我们当前的讨论无关:
'''
final.dropna(inplace=True)
# print(final.head())

data2010 = final.query("year == 2010 & ages == 'total'")
# print(data2010.head())

data2010.set_index('state', inplace=True)
density = data2010['population'] / data2010['area (sq. mi)']
density.sort_values(ascending=False, inplace=True)
print(density.head())
print(density.tail())

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715
error: Sorry,暂时内容不可复制!