标签归档:转载

Folium — 可能是 Python 最强的绘制地图神器

今天给大家介绍一个非常 NB 的Python 库,专门用来绘制地图的,它叫 Folium.

1. Folium简介

Folium是一个基于leaflet.js的Python地图库,其中,Leaflet是一个非常轻的前端地图可视化库。

即可以使用Python语言调用Leaflet的地图可视化能力。它不单单可以在地图上展示数据的分布图,还可以使用Vincent/Vega在地图上加以标记。

Folium可以让你用Python强大生态系统来处理数据,然后用Leaflet地图来展示。

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

2. Folium的使用

地图的生成

img

folium.folium.Map()详解

folium.folium.Map(location=None, width='100%', height='100%', left='0%', top='0%', position='relative', tiles='OpenStreetMap', attr=None, min_zoom=0, max_zoom=18, zoom_start=10, min_lat=-90, max_lat=90, min_lon=-180, max_lon=180, max_bounds=False, crs='EPSG3857', control_scale=False, prefer_canvas=False, no_touch=False, disable_3d=False, png_enabled=False, zoom_control=True, **kwargs)

参数说明

  • location (tuple or list, default None):纬度和经度

  • width (pixel int or percentage string (default: ‘100%’)):地图宽度

  • height (pixel int or percentage string (default: ‘100%’)):地图高度

  • tiles (str, default ‘OpenStreetMap’) :瓦片名称或使用TileLayer classass.

  • min_zoom (int, default 0):地图可缩放的最小级别

  • max_zoom (int, default 18):地图可缩放的最大级别

  • zoom_start (int, default 10) :地图的初始缩放级别

  • attr (string, default None):当使用自定义瓦片时,传入自定义瓦片的名词

  • crs (str, default ‘EPSG3857’) :投影坐标系标识

  • EPSG3857: Web墨卡托投影后的平面地图,坐标单位为米。大部分国外地图使用的时该标准。

  • EPSG4326: Web墨卡托投影后的平面地图,但仍然使用WGS84的经度、纬度表示坐标。

  • EPSG3395: 墨卡托投影,主要用于航海图

  • Simple: 简单的x,y匹配,用于自定义瓦片(比如游戏地图)

  • control_scale (bool, default False) :是否在地图上显示缩放标尺

  • prefer_canvas (bool, default False):强制使用Canvas渲染

  • no_touch (bool, default False) :是否允许触摸事件

  • disable_3d (bool, default False) :强制使用CSS 3D效果

  • zoom_control (bool, default True) :是否要限制zoom操作

  • **kwargs:Leaflets地图类的其他参数: https://leafletjs.com/reference-1.5.1.html#map

“tiles”参数可选值:

  • “OpenStreetMap”

  • “Mapbox Bright” (Limited levels of zoom for free tiles)

  • “Mapbox Control Room” (Limited levels of zoom for free tiles)

  • “Stamen” (Terrain, Toner, and Watercolor)

  • “Cloudmade” (Must pass API key)

  • “Mapbox” (Must pass API key)

  • “CartoDB” (positron and dark_matter)

“tiles”的自定义设置:

 

img

地球上同一个地理位置的经纬度,在不同的坐标系中,会有少量偏移,国内目前常见的坐标系主要分为三种:

  • 地球坐标系——WGS84:常见于GPS设备,Google地图等国际标准的坐标体系。

  • 火星坐标系——GCJ-02:中国国内使用的被强制加密后的坐标体系,高德坐标就属于该种坐标体系。

  • 百度坐标系——BD-09:百度地图所使用的坐标体系,是在火星坐标系的基础上又进行了一次加密处理。

所以在设置“tiles”时需要考虑目前手中得经纬度属于那种坐标系。

由于投影坐标系中没有GCJ-02和BD-09对应的标识,所以在自定义瓦片时主要经纬度能匹配上,crs中的设置可保持不变。更多详情介绍请看:瓦片坐标系学习

如果需要将地图保存,只需执行:m.save(“map.html”) 即可。

添加点、线、面要素

添加点

import folium
m = folium.Map(location=[39.917834116.397036], zoom_start=13, width='50%',height='50%', zoom_control='False',
               tiles='http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}&ltype=6',attr='AutoNavi')

tooltip ='请点击我查看该点信息'
folium.Marker([39.937282,116.403187], popup='南锣鼓巷',tooltip=tooltip).add_to(m)
folium.Marker([39.917834,116.397036], popup='故宫',tooltip=tooltip).add_to(m)
folium.Marker([39.928614,116.391746], popup='北海公园', tooltip=tooltip, icon=folium.Icon(color='red')).add_to(m)
folium.Marker([39.942143,116.382590], popup='后海公园', tooltip=tooltip, icon=folium.Icon(color='green', prefix='fa', icon='taxi')).add_to(m)

m

 

img

Folium.Icon类可以设置color, icon_color, icon, angle, prefix这5个参数:

  • color的可选项包括:[‘red’, ‘blue’, ‘green’, ‘purple’, ‘orange’, ‘darkred’, ‘lightred’, ‘beige’, ‘darkblue’, ‘darkgreen’, ‘cadetblue’, ‘darkpurple’, ‘white’, ‘pink’, ‘lightblue’, ‘lightgreen’, ‘gray’, ‘black’, ‘lightgray’] ,或者HTML颜色代码

  • icon_color同上

  • icon可以在Font-Awesome网站中找到对应的名字,并设置prefix参数为’fa’

  • angle以度为单位设置

其他:

m.add_child(folium.LatLngPopup()) #显示鼠标点击点经纬度
m.add_child(folium.ClickForMarker(popup='Waypoint')) # 将鼠标点击点添加到地图上

添加圆

folium.Circle(
    radius=300,
    location=[39.928614,116.391746],
    popup='北海公园',
    color='crimson',
    fill=False,
).add_to(m)
folium.CircleMarker(
    location=[39.942143,116.382590],
    radius=50,
    popup='后海公园',
    color='#3186cc',
    fill=True,
    fill_color='#3186cc'
).add_to(m)

 

img

Circle和CircleMarker的不同:CircleMarker的radius一个单位是像素,Circle的一个单位时米

添加线段

folium.PolyLine([
    [39.917834,116.397036],
    [39.928614,116.391746],
    [39.937282,116.403187],
    [39.942143,116.382590]
],color='red').add_to(m)

 

img

添加多边形

folium.Marker([39.917834,116.397036], popup='故宫').add_to(m)
folium.Marker([39.928614,116.391746], popup='北海公园').add_to(m)
folium.Marker([39.937282,116.403187], popup='南锣鼓巷').add_to(m)
folium.Marker([39.942143,116.382590], popup='后海公园').add_to(m)

folium.Polygon([
    [39.917834,116.397036],
    [39.928614,116.391746],
    [39.942143,116.382590],
    [39.937282,116.403187],
],color='blue', weight=2, fill=True, fill_color='blue', fill_opacity=0.3).add_to(m)

 

img

Folium的其他高级应用

在地图上显示前200条犯罪数据

import folium
import pandas as pd

san_map = folium.Map(location=[37.77-122.42], zoom_start=12,width='50%',height='50%')

# cdata = pd.read_csv('https://cocl.us/sanfran_crime_dataset')
cdata = pd.read_csv('Police_Department_Incidents_-_Previous_Year__2016_.csv'#犯罪数据,包含犯罪所在经纬度

# get the first 200 crimes in the cdata
limit = 200
data = cdata.iloc[0:limit, :]
# Instantiate a feature group for the incidents in the dataframe
incidents = folium.map.FeatureGroup()
# Loop through the 200 crimes and add each to the incidents feature group
for lat, lng, in zip(cdata.Y, data.X):
    incidents.add_child(
        folium.CircleMarker(
            [lat, lng],
            radius=7# define how big you want the circle markers to be
            color='yellow',
            fill=True,
            fill_color='red',
            fill_opacity=0.4
        )
    )

san_map.add_child(incidents)

 

img

统计区域犯罪总数

from folium import plugins

# let's start again with a clean copy of the map of San Francisco
san_map = folium.Map(location=[37.77-122.42], zoom_start=12,width='50%',height='50%')

# instantiate a mark cluster object for the incidents in the dataframe
incidents = plugins.MarkerCluster().add_to(san_map)

# loop through the dataframe and add each data point to the mark cluster
for lat, lng, label, in zip(data.Y, data.X, cdata.Category):
    folium.Marker(
        location=[lat, lng],
        icon=None,
        popup=label,
    ).add_to(incidents)

# add incidents to map
san_map.add_child(incidents)

 

img

以热力图的方式呈现

from folium.plugins import HeatMap

san_map = folium.Map(location=[37.77-122.42], zoom_start=12,width='50%',height='50%')

# Convert data format
heatdata = data[['Y','X']].values.tolist()

# add incidents to map
HeatMap(heatdata).add_to(san_map)

san_map

 

img

在地图上呈现GeoJSON边界数据

import json
import requests

# url = 'https://cocl.us/sanfran_geojson'
url = 'san-francisco.geojson'
san_geo = f'{url}'
san_map = folium.Map(location=[37.77-122.42], zoom_start=12,width='50%',height='50%')
folium.GeoJson(
    san_geo,
    style_function=lambda feature: {
        'fillColor''#ffff00',
        'color''blue',
        'weight'2,
        'dashArray''5, 5'
    }
).add_to(san_map)

san_map

 

img

在GeoJSON上绘制Choropleth分级着色图

# Count crime numbers in each neighborhood
disdata = pd.DataFrame(cdata['PdDistrict'].value_counts())
disdata.reset_index(inplace=True)
disdata.rename(columns={'index':'Neighborhood','PdDistrict':'Count'},inplace=True)

san_map = folium.Map(location=[37.77-122.42], zoom_start=12,width='50%',height='50%')

folium.Choropleth(
    geo_data=san_geo,
    data=disdata,
    columns=['Neighborhood','Count'],
    key_on='feature.properties.DISTRICT',
    #fill_color='red',
    fill_color='YlOrRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    highlight=True,
    legend_name='Crime Counts in San Francisco'
).add_to(san_map)

san_map

 

img

3. 各地图提供商瓦片服务地图规则

高德地图

目前高德的瓦片地址有如下两种:

  • http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7&ltype=1

  • http://webst0{1-4}.is.autonavi.com/appmaptile?style=7&x={x}&y={y}&z={z}

前者是高德的新版地址,后者是老版地址。

高德新版的参数:

  • lang:可以通过zh_cn设置中文,en设置英文

  • size:基本无作用

  • scl:瓦片尺寸控制,1=256,2=512

  • style:设置影像和路网,style=6为卫星图,style=7为街道图,style=8为标注图

  • ltype:线性控制,增加后,只对地图要素进行控制,没有文字注记,要素多少,是否透明

这些规律并不是绝对的,有可能有的组合某些参数不起作用。

谷歌地图

目前谷歌的瓦片地址也存在两种:

  • 国内:http://mt{0-3}.google.cn/vt/lyrs=m&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}

  • 国外:http://mt{0-3}.google.com/vt/lyrs=m&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}

参数详解:

  • lyrs = 类型

  • h = roads only 仅限道路

  • m = standard roadmap 标准路线图

  • p = terrain 带标签的地形图

  • r = somehow altered roadmap 某种改变的路线图

  • s = satellite only 仅限卫星

  • t = terrain only 仅限地形

  • y = hybrid 带标签的卫星图

  • gl = 坐标系

  • CN = 中国火星坐标系

  • hl = 地图文字语言

  • zh-CN = 中文

  • en-US = 英文

  • x = 瓦片横坐标

  • y = 瓦片纵坐标

  • z = 缩放级别 卫星图0-14,路线图0-17

百度地图

百度当前的瓦片地址:

  • http://online{0-4}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&udt=202004151&scaler=2&p=0

  • http://api{0-3}.map.bdimg.com/customimage/tile?&x={x}&y={y}&z={z}&udt=20180601&scale=1

  • http://its.map.baidu.com:8002/traffic/TrafficTileService?level={z}&x={x}&y={y}&time=1373790856265&label=web2D&;v=017

备注:瓦片地址中的x和y对应的并不是经纬度值,而是瓦片编号,中国主要地图商的瓦片编号流派:

目前百度的瓦片编号比较特殊,Folium暂不支持。

其他参考资料:

  • https://github.com/geometalab/pyGeoTile

  • https://github.com/anzhihun/OpenLayers3Primer/blob/master/ch05/05-03.md

  • http://www.winseliu.com/blog/2018/01/30/map-started-guide/

  • https://github.com/CntChen/tile-lnglat-transform

腾讯地图

腾讯地图的瓦片地图URL格式:

  • http://rt1.map.gtimg.com/realtimerender?z={z}&x={x}&y={y}&type=vector&style=0

由于腾讯地图使用的瓦片编码时TMS,所以使用时需要额外的设置。具体如下:

 

其他底图

 

  • {0,1,2,3}代表了url的subDomain,在请求时会随机的在url中使用mt0、mt1、mt2、mt3。{z}代表zoom,即缩放级别,{x}代表列号,{y}代表行号。

  • GeoQ 官网有公开的多个基于 ArcGIS 的地图服务,均可使用,详见https://map.geoq.cn/arcgis/rest/services

4. 参考链接:

  • https://leafletjs.com/

  • https://python-visualization.github.io/folium/

  • http://openwhatevermap.xyz/

作者:钱魏Way
原文:https://www.biaodianfu.com/folium.html

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

最全总结!一张图整理了 Python 所有内置异常

 

在编写程序时,可能会经常报出一些异常,很大一方面原因是自己的疏忽大意导致程序给出错误信息,另一方面是因为有些异常是程序运行时不可避免的,比如:在爬虫时可能有几个网页的结构不一致,这时两种结构的网页用同一套代码就会出错

所以,我们就需要捕获出现的异常,以防止程序因为错误信息而终止运行

Python 有很多的内置异常,也就是说 Python 开发者提前考虑到了用户编程过程中可能会出现这类错误,所以制造了这些内置异常可以快速准确向用户反馈出错信息帮助找出代码中的 Bug

Python 官方文档中也给出了所有内置异常及触发条件,为了更好的阅读体验,我把所有异常及触发条件整理成了一张思维导图:

文末附有高清版本的获取方式

伙伴们可以直接划至文末取图,下面针对几个常见的异常单独介绍一下,通过举例深入了解在什么条件下会触发哪一种异常。

1、SyntaxError

SyntaxError 主要是 Python 语法发生了错误,比如少个冒号、多个引号之类的,编程时稍微疏忽大意一下就会出错,应该是最常见的一种异常错误了

In [1]: While True print(‘1’)
  File “<ipython-input-1-8ebf67bb4c2b>”, line 1
    While True print(‘1’)
          ^
SyntaxError: invalid syntax

2、TypeError

TypeError 是类型错误,也就是说将某个操作或功能应用于不合适类型的对象时引发,比如整型与字符型进行加减法、在两个列表之间进行相减操作等等

In [8]: a = [1,2];b = [2,3]
In [9]: a-b
—————————————————————————
TypeError                                 Traceback (most recent call last)
<ipython-input-9-5ae0619f8fe1> in <module>
—-> 1 a-b

TypeError: unsupported operand type(s) for -: ‘list’ and ‘list’

3、IndexError

IndexError 是指索引出现了错误,比如最常见下标索引超出了序列边界,比如当某个序列 m 只有三个元素,却试图访问 m[4]

In [16]: m = [1,2,3]
In [17]: m[4]
—————————————————————————
IndexError                                Traceback (most recent call last)
<ipython-input-17-94e0dfab3ff6> in <module>
—-> 1 m[4]

IndexError: list index out of range

4、KeyError

KeyError 是关键字错误,这个异常主要发生在字典中,比如当用户试图访问一个字典中不存在的键时会被引发

In [18]: dict_ = {‘1’:‘yi’,‘2’:‘er’}
In [19]: dict_[‘3’]
—————————————————————————
KeyError                                  Traceback (most recent call last)
<ipython-input-19-c2e43847635f> in <module>
—-> 1 dict_[‘3’]

KeyError: ‘3’

5、ValueError

ValueError 为值错误,当用户传入一个调用者不期望的值时会引发,即使这个值的类型是正确的,比如想获取一个列表中某个不存在值的索引

In [22]: n = [1,2,3]
In [23]: n.index(4)
—————————————————————————
ValueError                                Traceback (most recent call last)
<ipython-input-23-9a1887cf29d7> in <module>
—-> 1 n.index(4)

ValueError: 4 is not in list

6、AttributeError

AttributeError 是属性错误,当用户试图访问一个对象不存在的属性时会引发,比如列表有 index 方法,而字典却没有,所以对一个字典对象调用该方法就会引发该异常

In [25]: dict_ = {‘1’:‘yi’,‘2’:‘er’}
In [26]: dict_.index(‘1’)
—————————————————————————
AttributeError                            Traceback (most recent call last)
<ipython-input-26-516844ad2563> in <module>
—-> 1 dict_.index(‘1’)

AttributeError: ‘dict’ object has no attribute ‘index’

7、NameError

NameError 是指变量名称发生错误,比如用户试图调用一个还未被赋值或初始化的变量时会被触发

In [27]: print(list_)
—————————————————————————
NameError                                 Traceback (most recent call last)
<ipython-input-27-87ebf02ffcab> in <module>
—-> 1 print(list_)

NameError: name ‘list_’ is not defined

8、FileNotFoundError

FileNotFoundError 为打开文件错误,当用户试图以读取方式打开一个不存在的文件时引发

In [29]: fb = open(‘./list’,‘r’)
—————————————————————————
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-29-1b65fe5400ea> in <module>
—-> 1 fb = open(‘./list’,‘r’)

FileNotFoundError: [Errno 2] No such file or directory: ‘./list’

9、StopIteration

StopIteration 为迭代器错误,当访问至迭代器最后一个值时仍然继续访问,就会引发这种异常,提醒用户迭代器中已经没有值可供访问了

In [30]: list1 = [1,2]
In [31]: list2 = iter(list1)
In [33]: next(list2)
Out[33]: 1

In [34]: next(list2)
Out[34]: 2

In [35]: next(list2)
—————————————————————————
StopIteration                             Traceback (most recent call last)
<ipython-input-35-5a5a8526e73b> in <module>
—-> 1 next(list2)

10、AssertionError

AssertionError 为断言错误,当用户利用断言语句检测异常时,如果断言语句检测的表达式为假,则会引发这种异常

In [45]: list3 = [1,2]

In [46]: assert len(list3)>2
—————————————————————————
AssertionError                            Traceback (most recent call last)
<ipython-input-46-ffd051e2ba94> in <module>
—-> 1 assert len(list3)>2

AssertionError:

上面这些异常应该是平时编程中遇见频率比较高的一部分,完整的还是要看上文的思维导图或者查阅官方文档,当然除此之外,Python 也支持用户根据自己的需求自定义异常,这里就不再过多概述了。

对于异常的处理 Python 也有着比较强大的功能,比如可以捕获异常,主动抛出异常等等,主要有下面几种方式:

  • 1.try … except 结构语句捕获
  • 2.try … except … finally 结构语句捕获
  • 3.try … except … else 结构语句捕获
  • 4.raise关键字主动抛出异常
  • 5.try … raise … except 触发异常
  • 6.assert断言语句
  • 7.traceback模块跟踪查看异常

除了已经下载好的思维导图,也有一份在线版思维导图,我是用百度脑图绘制的

如果你觉得导图有哪部分不合理的话,可以根据自己的想法在网页端在线编辑,左下角阅读原文是参考的官方文档链接

本文转自AirPython.

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 除了 time.sleep,你还有 event.wait 暂停代码的方法

我们知道,在 Python 里面可以使用time.sleep来让代码暂停一段时间,例如:

import time

print('...部分代码...')
time.sleep(5)
print('...剩下的代码...')

程序首先打印出...部分代码...,然后等待5秒钟,再打印出...剩下的代码...

现在大家想一想,有没有什么办法,在不使用time.sleep的情况下,让程序暂停5秒?

你可能会说,用requests访问一个延迟5秒的网址、或者用递归版算法计算斐波那契数列第36位……这些奇技淫巧。

不过今天我说的,是另外一个东西,threading模块里面的Event

我们来看看它的用法:

import threading

event = threading.Event()
print('...部分代码...')
event.wait(5)
print('...剩下的代码...')

这样一来,程序首先打印出...部分代码...,然后等待5秒钟,再打印出...剩下的代码...

功能看起来跟time.sleep没什么区别,那为什么我要特别提到它呢?因为在多线程里面,它比time.sleep更有用。我们来看一个例子:

import threading

class Checker(threading.Thread):
def __init__(self, event):
super().__init__()
self.event = event

def run(self):
while not self.event.is_set():
print('检查 redis 是否有数据')
       time.sleep(60)

trigger_async_task()
event = threading.Event()
checker = Checker(event)
checker.start()
if user_cancel_task():
    event.set()

我来解释一下这段代码的意思。在主线程里面,我调用trigger_async_task()触发了一个异步任务。这个任务多久完成我并不清楚。但是这个任务完成以后,它会往 Redis 里面发送一条消息,只要 Redis 有这个消息了,我就知道它完成了。所以我要创建一个 checker 子线程,每60秒去 Redis里面检查任务是否完成。如果没有完成,就暂停60秒,然后再检查。

但某些情况下,我不需要等待了,例如用户主动取消了任务。这个时候,我就想提前结束这个 checker 子线程。

但是我们知道,线程是不能从外面主动杀死的,只能让它自己退出。所以当我执行event.set()后,子线程里面self.event.is_set()就会返回 False,于是这个循环就不会继续执行了。

可是,如果某一轮循环刚刚开始,我在主线程里面调用了event.set()。此时,子线程还在time.sleep中,那么子线程需要等待60秒才会退出。

但如果我修改一下代码,使用self.event.wait(60)

import threading

class Checker(threading.Thread):
def __init__(self, event):
        super().__init__()
        self.event = event

def run(self):
while not self.event.is_set():
            print('检查 redis 是否有数据')
            self.event.wait(60)

trigger_task()
event = threading.Event()
checker = Checker(event)
checker.start()
if user_cancel_task():
    event.set()

那么,即便self.event.wait(60)刚刚开始阻塞,只要我在主线程中执行了event.set(),子线程里面的阻塞立刻就会结束。于是子线程立刻就会结束。不需要再白白等待60秒。

并且,event.wait()这个函数在底层是使用 C 语言实现的,不受 GIL 锁的干扰。

本文转自AirPython.

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

数据分析实战教程|Pandas处理数据太慢,来试试Polars吧!

很多人在学习数据分析的时候,肯定都会用到Pandas这个库,非常的实用!

从创建数据到读取各种格式的文件(text、csv、json),或者对数据进行切片和分割组合多个数据源,Pandas都能够很好的满足。

Pandas最初发布于2008年,使用Python、Cython和C编写的。是一个超级强大、快速易于使用的Python库,用于数据分析和处理。

当然Pandas也是有不足之处的,比如不具备多处理器,处理较大的数据集速度很慢。

今天,小F就给大家介绍一个新兴的Python库——Polars。

使用语法和Pandas差不多,处理数据的速度却比Pandas快了不少。

一个是大熊猫,一个是北极熊~

GitHub地址:https://github.com/ritchie46/polars

使用文档:https://ritchie46.github.io/polars-book/

Polars是通过Rust编写的一个库,Polars的内存模型是基于Apache Arrow。

Polars存在两种API,一种是Eager API,另一种则是Lazy API。

其中Eager API和Pandas的使用类似,语法差不太多,立即执行就能产生结果。

而Lazy API就像Spark,首先将查询转换为逻辑计划,然后对计划进行重组优化,以减少执行时间和内存使用。

安装Polars,使用百度pip源。

# 安装polars
pip install polars -i https://mirror.baidu.com/pypi/simple/

安装成功后,开始测试,比较Pandas和Polars处理数据的情况。

使用某网站注册用户的用户名数据进行分析,包含约2600万个用户名的CSV文件。

import pandas as pd

df = pd.read_csv(‘users.csv’)
print(df)

数据情况如下。

此外还使用了一个自己创建的CSV文件,用以数据整合测试。

import pandas as pd

df = pd.read_csv(‘fake_user.csv’)
print(df)

得到结果如下。

首先比较一下两个库的排序算法耗时。

import timeit
import pandas as pd

start = timeit.default_timer()

df = pd.read_csv(‘users.csv’)
df.sort_values(‘n’, ascending=False)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

————————-
Time:  27.555776743218303

可以看到使用Pandas对数据进行排序,花费了大约28s。

import timeit
import polars as pl

start = timeit.default_timer()

df = pl.read_csv(‘users.csv’)
df.sort(by_column=‘n’, reverse=True)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

———————–
Time:  9.924110282212496

Polars只花费了约10s,这意味着Polars比Pandas快了2.7倍。

下面,我们来试试数据整合的效果,纵向连接。

import timeit
import pandas as pd

start = timeit.default_timer()

df_users = pd.read_csv(‘users.csv’)
df_fake = pd.read_csv(‘fake_user.csv’)
df_users.append(df_fake, ignore_index=True)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

————————
Time:  15.556222308427095

使用Pandas耗时15s。

import timeit
import polars as pl

start = timeit.default_timer()

df_users = pl.read_csv(‘users.csv’)
df_fake = pl.read_csv(‘fake_user.csv’)
df_users.vstack(df_fake)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

———————–
Time:  3.475433263927698

Polars居然最使用了约3.5s,这里Polars比Pandas快了4.5倍。

通过上面的比较,Polars在处理速度上表现得相当不错。

可以是大家在未来处理数据时,另一种选择~

当然,Pandas目前历时12年,已经形成了很成熟的生态,支持很多其它的数据分析库。

Polars则是一个较新的库,不足的地方还有很多。

如果你的数据集对于Pandas来说太大,对于Spark来说太小,那么Polars便是你可以考虑的一个选择。

本文转载自公众号【法纳斯特】

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

一行代码实战,用Python玩转所有的 emoji

本文简介

还记得刚刚玩儿QQ、微信时,表情轰炸的场景吗?
  • 小时候,快乐是件很简单的事儿!
  • 长大后,简单是件很快乐的事儿!
随着时间的推移,有些表情被淘汰了,有的表情被保留了下来。慢慢地,它似乎可以代替我们说话人的表情和语气了,它好像成为了社交必备。你或许还看过,微信表情可以当作证据提交,这样的新闻。
“表情”很好玩,我们可以随时随地用它表达我们此时此刻的情感;

“表情”很烦人,有些表情确实让人挺尴尬。

就是玩儿,反正玩儿 也不犯罪,大家开心就好。今天黄同学就带大家讲述如何用Python玩转“表情”。

安装emoji库

emoji库,属于第三方库。在使用之前,我们需要提前安装和导入。

① emoji库的安装

pip install emoji -i https://pypi.tuna.tsinghua.edu.cn/simple/
当出现successfully表示安装成功。

②emoji库的导入

import emoji

玩儿起来

emoji中主要就两个函数,供大家玩耍,分别是:
  • emojize():根据 code 生成 emoji 表情;
  • demojize():将 emoji 表情解码为code;
注意哦:默认情况,你只能使用一部分表情。很多表情,需要加参数use_aliases=True后,才可以展示。
昨天没睡好,今天好困呀!
emoji.emojize(“昨天没睡好,今天:zzz:呀”)
结果如下:
笑逐颜开,好彩自然来!
emoji.emojize(“:smile:逐颜开,好彩自然来!”,use_aliases=True)
结果如下:
黄同学是白羊座!
emoji.emojize(“黄同学是:aries:”,use_aliases=True)
结果如下:
小明是AB型血型!
emoji.emojize(“小明是:ab:型血型!”,use_aliases=True)
结果如下:
晚上十一点了!
emoji.emojize(“晚上:clock11:了!”,use_aliases=True)
结果如下:
其实这个库有很多表情包了,部分截图如下:
这里直接给大家上官网吧,大家可以自行下去玩玩儿。https://pypi.org/project/emoji/
本文转自快学Python

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

利用 Python pyttsx3 将 PDF 文档转为语音音频

 

 

 

 

 

 

 

 

1. 转语音工具

微信读书里的电子书有配套的自动音频,而且声音优化的不错,比传统的机械朗读听起来舒服很多

记得之前看到过 Python有一个工具包,可以将文字转换为语音,支持英文和中文的同时,还能调节语速语调、导出 mp3 等音频文件

去 Github 查了下,这个依赖库叫:pyttsx3

简单来说,pyttsx3 可以文字转语音,且是离线工作的,这一点就很实用

安装比较容易,直接在命令行用 pip 安装:

pip install pyttsx3

我准备动手试试,将 PDF 书籍转成音频

用什么书呢?最近在群里看到有人发张磊的新作《价值》电子书,这本今年刚出的畅销书盗版猖獗,我之前在微信读书里看过,对作者长期主义的观点深信不疑

那就它了

2. PDF转文本

肯定需要先读取 PDF 中的文字,再利用 pyttsx3 转语音

Python 中操作 PDF 的工具库主要是 PyPDF2,但发现编码实在有点繁琐

我就换了另一个库 pdfplumber,它与 PyPDF2 语法类似,用起来还算流畅

pdfplumber 可以处理 PDF 包括文本、表格、格式在内的各种信息,小而强大

# 读取PDF文档
pdf = pdfplumber.open("价值.pdf")

# 获取页数
print("总页数:",len(pdf.pages))
print("-----------------------------------------")

# 读取第4页
first_page = pdf.pages[3]
print("本页:",first_page.page_number+1)
print("-----------------------------------------")

# 导出第4页文本
text = first_page.extract_text()
print(text)

输出文字内容如下:

上面的代码做了几件事情:「读取 PDF 文档、读取页数、读取第 4 页、输出第 4 页文本」

3. 文本转语音

接下来开始将第 4 页的文本转化为音频

import pyttsx3

# 初始化来获取语音引擎
engine = pyttsx3.init()

# 去掉文本中的换行符
text = text.replace('\n','')

# 朗读文本
engine.say(text)
engine.runAndWait()

上面代码使用 pyttsx3 将文本转化为音频,然后朗读出来

我是在 jupyter notebook 上做实验的,代码执行后,电脑会直接朗读

最后,将生成的音频保存为 mp3 格式

# 保存音频到本地,格式为mp3
engine.save_to_file(text, 'test.mp3')
engine.runAndWait()

当然,你还可以调整声音的类型、速度、大小

# 调整人声类型
voices = engine.getProperty('voices')  
engine.setProperty('voice', voices[0].id)

# 调整语速,范围一般在0~500之间
rate = engine.getProperty('rate')                         
engine.setProperty('rate'200)     

# 调整声量,范围在0~1之间
volume = engine.getProperty('volume')                         
engine.setProperty('volume',0.8

最后听下生成的语音是什么样的?

test.mp3 来自AirPython 01:58

整体比较流畅,虽然不如微信读书语音听着那般舒服,但做做小工具还是不错的

转自AirPython.

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 教程 — 将微信头像秒变成表情包

在日常生活中,我们经常会存取一些朋友们的丑照,在这个项目中,我们以萌萌哒的熊猫头作为背景,然后试着在背景图上加入朋友们的照片。效果如下图所示:

二、实现步骤

  1. 导入朋友的照片(前景照片);

  2. 处理前景照片(缩放、旋转,填充);

  3. 导入熊猫头照片(背景照片);

  4. 将前景和背景拼接起来形成表情包;

  5. 在表情包下面添加文字。

三、Python 实现

1、导入需要的库

import cv2
import numpy as mp
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont

这个项目主要是通过 opencv 完成,但如果要在表情包下面写中文的话,PIL(pillow)库是必不可少的。

2、绘图函数

这里写一个绘图函数,方便绘图操作。

def plt_show(img):
    imageRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    plt.imshow(imageRGB)
    plt.show()

3、导入前景照片

image = cv2.imread('SXC.jpg', 0)  # 导入灰度图即可
plt_show(image)

4、等比例缩放前景照片

因为我们发现前景照片的尺寸比背景尺寸还要大,这显然是不合适的,所以要先对其进行等比例(0.3)缩放。

image_resize = cv2.resize(image, None, fx=0.3, fy=0.3, interpolation = cv2.INTER_CUBIC)
plt_show(image_resize)

5、对前景照片进行二值化处理

在这里,我们将像素值大于 80 的区域设置为 255;小于 80 的区域设置成 0。

ret, image_binary = cv2.threshold(image_resize, 80, 255, cv2.THRESH_BINARY)
plt_show(image_binary)

6、提取出感兴趣区域

image_roi = image_binary[74: 185, 0: 150]
plt_show(image_roi)

7、旋转图片

因为我们的背景图片(熊猫头)是正的,而前景图片有些向右倾斜,所以要先对其进行旋转操作(大概逆时针旋转 15 度即可)。

rows, cols = image_roi.shape
M = cv2.getRotationMatrix2D(((cols-1)/2.0, (rows-1)/2.0), 15, 1)  # (旋转中心,逆时针旋转角度,各个方向同等扩大比例)
image_rotate = cv2.warpAffine(image_roi, M, (140, 130))  # (140, 130) 是指旋转后的画布大小
plt_show(image_rotate)

8、将一些不需要的黑色区域删除掉

在这里我们使用 cv2.fillPoly 函数对不需要的区域用白色进行填充。

h, w = image_rotate.shape

image_rotate_copy = image_rotate.copy()
pts1 = np.array([[0, 20],  [64, 0], [0, 0]], np.int32)
pts2 = np.array([[0, 18],  [0, h], [80, h]], np.int32)
pts3 = np.array([[0, 100],  [0, h], [w, h], [w, 100]], np.int32)
pts4 = np.array([[111, 0],  [w, 0], [w, 30]], np.int32)
pts5 = np.array([[124, 0],  [115, h], [w, h]], np.int32)
pts6 = np.array([[120, 40],  [95, 100], [120, 100]], np.int32)
foreground = cv2.fillPoly(image_rotate_copy, [pts1], (255, 255, 255))  # (图片,填充区域,填充颜色)
foreground = cv2.fillPoly(image_rotate_copy, [pts2], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts3], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts4], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts5], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts6], (255, 255, 255))

plt_show(foreground)

9、再次提取感兴趣区域并缩放

foreground_roi = foreground[0: 93, 0: 125]
plt_show(foreground_roi)

foreground_roi_resize = cv2.resize(foreground_roi, None, fx=2.5, fy=2.5, interpolation = cv2.INTER_CUBIC)
plt_show(foreground_roi_resize)

10、导入背景图片

background = cv2.imread('back.jpg', 0)
plt_show(background)

11、组合两张图片成表情包

h_f, w_f = foreground.shape
h_b, w_b = background.shape

left = (w_b - w_f)//2  # 前景图片在背景图片中的左边的横坐标
right = left + w_f  # 前景图片在背景图片中的右边的横坐标
top = 100  # 前景图片在背景图片中的上边的纵坐标
bottom = top + h_f  # 前景图片在背景图片中的下边的纵坐标

emoji = background
emoji[top: bottom, left: right] = foreground
plt_show(emoji)

12、在表情包下面添加文本

12.1 添加英文文本

如果只是要添加英文文本,用 opencv 就可以解决:

emoji_copy = emoji.copy()
# (图片,文本,位置,字体,文本大小,文本颜色,文本粗细)
cv2.putText(emoji_copy, "FXXK!!", (210, 500), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 0), 5)
plt_show(emoji_copy)

12.2 添加中文文本

如果要添加中文文本,我们需要借助 PIL 库来实现。

PilImg = Image.fromarray(emoji)  # cv2 转 PIL
draw = ImageDraw.Draw(PilImg)  # 创建画笔
ttfront = ImageFont.truetype('simhei.ttf', 34)  # 设置字体
draw.text((210, 450),"你瞅啥!!",fill=0, font=ttfront)  # (位置,文本,文本颜色,字体)
emoji_text = cv2.cvtColor(np.array(PilImg),cv2.COLOR_RGB2BGR)  # PIL 转回 cv2
plt_show(emoji_text)

13、保存表情包

cv2.imwrite('./emoji.png', np.array(emoji_text))

四、完整代码

import cv2
import numpy as mp
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont

def plt_show(img):
    imageRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    plt.imshow(imageRGB)
    plt.show()

image = cv2.imread('SXC.jpg', 0)  # 导入前景图片

image_resize = cv2.resize(image, None, fx=0.3, fy=0.3, interpolation = cv2.INTER_CUBIC)  # 缩放

ret, image_binary = cv2.threshold(image_resize, 80, 255, cv2.THRESH_BINARY)  # 图片二值化

image_roi = image_binary[74: 185, 0: 150]  # 感兴趣区域

rows, cols = image_roi.shape
# 旋转
M = cv2.getRotationMatrix2D(((cols-1)/2.0, (rows-1)/2.0), 15, 1)
image_rotate = cv2.warpAffine(image_roi, M, (140, 130))
# 填充不需要的区域
h, w = image_rotate.shape
image_rotate_copy = image_rotate.copy()
pts1 = np.array([[0, 20],  [64, 0], [0, 0]], np.int32)
pts2 = np.array([[0, 18],  [0, h], [80, h]], np.int32)
pts3 = np.array([[0, 100],  [0, h], [w, h], [w, 100]], np.int32)
pts4 = np.array([[111, 0],  [w, 0], [w, 30]], np.int32)
pts5 = np.array([[124, 0],  [115, h], [w, h]], np.int32)
pts6 = np.array([[120, 40],  [95, 100], [120, 100]], np.int32)
foreground = cv2.fillPoly(image_rotate_copy, [pts1], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts2], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts3], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts4], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts5], (255, 255, 255))
foreground = cv2.fillPoly(image_rotate_copy, [pts6], (255, 255, 255))

foreground_roi = foreground[0: 93, 0: 125]
foreground_roi_resize = cv2.resize(foreground_roi, None, fx=2.5, fy=2.5, interpolation = cv2.INTER_CUBIC)

background = cv2.imread('back.jpg', 0)  # 导入背景图片
# 拼接两张图片
h_f, w_f = foreground_roi_resize.shape
h_b, w_b = background.shape
left = (w_b - w_f)//2
right = left + w_f
top = 80
bottom = top + h_f
emoji = background
emoji[top: bottom, left: right] = foreground_roi_resize

PilImg = Image.fromarray(emoji)  # cv2 转 PIL
draw = ImageDraw.Draw(PilImg)  # 创建画笔
ttfront = ImageFont.truetype('simhei.ttf', 34)  # 设置字体
draw.text((210, 450),"你瞅啥!!",fill=0, font=ttfront)  # (位置,文本,文本颜色,字体)
emoji_text = cv2.cvtColor(np.array(PilImg),cv2.COLOR_RGB2BGR)  # PIL 转回 cv2

cv2.imwrite('./emoji.png', np.array(emoji_text))  # 保存表情包

原文链接:https://urlify.cn/2aQfya

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 优化提速的 8 个小技巧

 

   作者:张皓

   来源:https://zhuanlan.zhihu.com/p/143052860

Python 是一种脚本语言,相比 C/C++ 这样的编译语言,在效率和性能方面存在一些不足。但是,有很多时候,Python 的效率并没有想象中的那么夸张。本文对一些 Python 代码加速运行的技巧进行整理。

0. 代码优化原则

本文会介绍不少的 Python 代码加速运行的技巧。在深入代码优化细节之前,需要了解一些代码优化基本原则。

第一个基本原则是不要过早优化。很多人一开始写代码就奔着性能优化的目标,“让正确的程序更快要比让快速的程序正确容易得多”。因此,优化的前提是代码能正常工作。过早地进行优化可能会忽视对总体性能指标的把握,在得到全局结果前不要主次颠倒。

第二个基本原则是权衡优化的代价。优化是有代价的,想解决所有性能的问题是几乎不可能的。通常面临的选择是时间换空间或空间换时间。另外,开发代价也需要考虑。

第三个原则是不要优化那些无关紧要的部分。如果对代码的每一部分都去优化,这些修改会使代码难以阅读和理解。如果你的代码运行速度很慢,首先要找到代码运行慢的位置,通常是内部循环,专注于运行慢的地方进行优化。在其他地方,一点时间上的损失没有什么影响。

1. 避免全局变量

# 不推荐写法。代码耗时:26.8秒
import math

size = 10000
for x in range(size):
    for y in range(size):
        z = math.sqrt(x) + math.sqrt(y)

许多程序员刚开始会用 Python 语言写一些简单的脚本,当编写脚本时,通常习惯了直接将其写为全局变量,例如上面的代码。但是,由于全局变量和局部变量实现方式不同,定义在全局范围内的代码运行速度会比定义在函数中的慢不少。通过将脚本语句放入到函数中,通常可带来 15% – 30% 的速度提升。

# 推荐写法。代码耗时:20.6秒
import math

def main():  # 定义到函数中,以减少全部变量使用
    size = 10000
    for x in range(size):
        for y in range(size):
            z = math.sqrt(x) + math.sqrt(y)

main()

2. 避免.

2.1 避免模块和函数属性访问

# 不推荐写法。代码耗时:14.5秒
import math

def computeSqrt(size: int):
    result = []
    for i in range(size):
        result.append(math.sqrt(i))
    return result

def main():
    size = 10000
    for _ in range(size):
        result = computeSqrt(size)

main()

每次使用.(属性访问操作符时)会触发特定的方法,如__getattribute__()__getattr__(),这些方法会进行字典操作,因此会带来额外的时间开销。通过from import语句,可以消除属性访问。

# 第一次优化写法。代码耗时:10.9秒
from math import sqrt

def computeSqrt(size: int):
    result = []
    for i in range(size):
        result.append(sqrt(i))  # 避免math.sqrt的使用
    return result

def main():
    size = 10000
    for _ in range(size):
        result = computeSqrt(size)

main()

在第 1 节中我们讲到,局部变量的查找会比全局变量更快,因此对于频繁访问的变量sqrt,通过将其改为局部变量可以加速运行。

# 第二次优化写法。代码耗时:9.9秒
import math

def computeSqrt(size: int):
    result = []
    sqrt = math.sqrt  # 赋值给局部变量
    for i in range(size):
        result.append(sqrt(i))  # 避免math.sqrt的使用
    return result

def main():
    size = 10000
    for _ in range(size):
        result = computeSqrt(size)

main()

除了math.sqrt外,computeSqrt函数中还有.的存在,那就是调用listappend方法。通过将该方法赋值给一个局部变量,可以彻底消除computeSqrt函数中for循环内部的.使用。

# 推荐写法。代码耗时:7.9秒
import math

def computeSqrt(size: int):
    result = []
    append = result.append
    sqrt = math.sqrt    # 赋值给局部变量
    for i in range(size):
        append(sqrt(i))  # 避免 result.append 和 math.sqrt 的使用
    return result

def main():
    size = 10000
    for _ in range(size):
        result = computeSqrt(size)

main()

2.2 避免类内属性访问

# 不推荐写法。代码耗时:10.4秒
import math
from typing import List

class DemoClass:
    def __init__(self, value: int):
        self._value = value
    
    def computeSqrt(self, size: int) -> List[float]:
        result = []
        append = result.append
        sqrt = math.sqrt
        for _ in range(size):
            append(sqrt(self._value))
        return result

def main():
    size = 10000
    for _ in range(size):
        demo_instance = DemoClass(size)
        result = demo_instance.computeSqrt(size)

main()

避免.的原则也适用于类内属性,访问self._value的速度会比访问一个局部变量更慢一些。通过将需要频繁访问的类内属性赋值给一个局部变量,可以提升代码运行速度。

# 推荐写法。代码耗时:8.0秒
import math
from typing import List

class DemoClass:
    def __init__(self, value: int):
        self._value = value
    
    def computeSqrt(self, size: int) -> List[float]:
        result = []
        append = result.append
        sqrt = math.sqrt
        value = self._value
        for _ in range(size):
            append(sqrt(value))  # 避免 self._value 的使用
        return result

def main():
    size = 10000
    for _ in range(size):
        demo_instance = DemoClass(size)
        demo_instance.computeSqrt(size)

main()

3. 避免不必要的抽象

# 不推荐写法,代码耗时:0.55秒
class DemoClass:
    def __init__(self, value: int):
        self.value = value

    @property
    def value(self) -> int:
        return self._value

    @value.setter
    def value(self, x: int):
        self._value = x

def main():
    size = 1000000
    for i in range(size):
        demo_instance = DemoClass(size)
        value = demo_instance.value
        demo_instance.value = i

main()

任何时候当你使用额外的处理层(比如装饰器、属性访问、描述器)去包装代码时,都会让代码变慢。大部分情况下,需要重新进行审视使用属性访问器的定义是否有必要,使用getter/setter函数对属性进行访问通常是 C/C++ 程序员遗留下来的代码风格。如果真的没有必要,就使用简单属性。

# 推荐写法,代码耗时:0.33秒
class DemoClass:
    def __init__(self, value: int):
        self.value = value  # 避免不必要的属性访问器

def main():
    size = 1000000
    for i in range(size):
        demo_instance = DemoClass(size)
        value = demo_instance.value
        demo_instance.value = i

main()

4. 避免数据复制

4.1 避免无意义的数据复制

# 不推荐写法,代码耗时:6.5秒
def main():
    size = 10000
    for _ in range(size):
        value = range(size)
        value_list = [x for x in value]
        square_list = [x * x for x in value_list]

main()

上面的代码中value_list完全没有必要,这会创建不必要的数据结构或复制。

# 推荐写法,代码耗时:4.8秒
def main():
    size = 10000
    for _ in range(size):
        value = range(size)
        square_list = [x * x for x in value]  # 避免无意义的复制

main()

另外一种情况是对 Python 的数据共享机制过于偏执,并没有很好地理解或信任 Python 的内存模型,滥用 copy.deepcopy()之类的函数。通常在这些代码中是可以去掉复制操作的。

4.2 交换值时不使用中间变量

# 不推荐写法,代码耗时:0.07秒
def main():
    size = 1000000
    for _ in range(size):
        a = 3
        b = 5
        temp = a
        a = b
        b = temp

main()

上面的代码在交换值时创建了一个临时变量temp,如果不借助中间变量,代码更为简洁、且运行速度更快。

# 推荐写法,代码耗时:0.06秒
def main():
    size = 1000000
    for _ in range(size):
        a = 3
        b = 5
        a, b = b, a  # 不借助中间变量

main()

4.3 字符串拼接用join而不是+

# 不推荐写法,代码耗时:2.6秒
import string
from typing import List

def concatString(string_list: List[str]) -> str:
    result = ''
    for str_i in string_list:
        result += str_i
    return result

def main():
    string_list = list(string.ascii_letters * 100)
    for _ in range(10000):
        result = concatString(string_list)

main()

当使用a + b拼接字符串时,由于 Python 中字符串是不可变对象,其会申请一块内存空间,将ab分别复制到该新申请的内存空间中。因此,如果要拼接 n 个字符串,会产生 n-1 个中间结果,每产生一个中间结果都需要申请和复制一次内存,严重影响运行效率。而使用join()拼接字符串时,会首先计算出需要申请的总的内存空间,然后一次性地申请所需内存,并将每个字符串元素复制到该内存中去。

# 推荐写法,代码耗时:0.3秒
import string
from typing import List

def concatString(string_list: List[str]) -> str:
    return ''.join(string_list)  # 使用 join 而不是 +

def main():
    string_list = list(string.ascii_letters * 100)
    for _ in range(10000):
        result = concatString(string_list)

main()

5. 利用if条件的短路特性

# 不推荐写法,代码耗时:0.05秒
from typing import List

def concatString(string_list: List[str]) -> str:
    abbreviations = {'cf.''e.g.''ex.''etc.''flg.''i.e.''Mr.''vs.'}
    abbr_count = 0
    result = ''
    for str_i in string_list:
        if str_i in abbreviations:
            result += str_i
    return result

def main():
    for _ in range(10000):
        string_list = ['Mr.''Hat''is''Chasing''the''black''cat''.']
        result = concatString(string_list)

main()

if 条件的短路特性是指对if a and b这样的语句, 当aFalse时将直接返回,不再计算b;对于if a or b这样的语句,当aTrue时将直接返回,不再计算b。因此, 为了节约运行时间,对于or语句,应该将值为True可能性比较高的变量写在or前,而and应该推后。

# 推荐写法,代码耗时:0.03秒
from typing import List

def concatString(string_list: List[str]) -> str:
    abbreviations = {'cf.''e.g.''ex.''etc.''flg.''i.e.''Mr.''vs.'}
    abbr_count = 0
    result = ''
    for str_i in string_list:
        if str_i[-1] == '.' and str_i in abbreviations:  # 利用 if 条件的短路特性
            result += str_i
    return result

def main():
    for _ in range(10000):
        string_list = ['Mr.''Hat''is''Chasing''the''black''cat''.']
        result = concatString(string_list)

main()

6. 循环优化

6.1 用for循环代替while循环

# 不推荐写法。代码耗时:6.7秒
def computeSum(size: int) -> int:
    sum_ = 0
    i = 0
    while i < size:
        sum_ += i
        i += 1
    return sum_

def main():
    size = 10000
    for _ in range(size):
        sum_ = computeSum(size)

main()

Python 的for循环比while循环快不少。

# 推荐写法。代码耗时:4.3秒
def computeSum(size: int) -> int:
    sum_ = 0
    for i in range(size):  # for 循环代替 while 循环
        sum_ += i
    return sum_

def main():
    size = 10000
    for _ in range(size):
        sum_ = computeSum(size)

main()

6.2 使用隐式for循环代替显式for循环

针对上面的例子,更进一步可以用隐式for循环来替代显式for循环

# 推荐写法。代码耗时:1.7秒
def computeSum(size: int) -> int:
    return sum(range(size))  # 隐式 for 循环代替显式 for 循环

def main():
    size = 10000
    for _ in range(size):
        sum = computeSum(size)

main()

6.3 减少内层for循环的计算

# 不推荐写法。代码耗时:12.8秒
import math

def main():
    size = 10000
    sqrt = math.sqrt
    for x in range(size):
        for y in range(size):
            z = sqrt(x) + sqrt(y)

main() 

上面的代码中sqrt(x)位于内侧for循环, 每次训练过程中都会重新计算一次,增加了时间开销。

# 推荐写法。代码耗时:7.0秒
import math

def main():
    size = 10000
    sqrt = math.sqrt
    for x in range(size):
        sqrt_x = sqrt(x)  # 减少内层 for 循环的计算
        for y in range(size):
            z = sqrt_x + sqrt(y)

main() 

7. 使用numba.jit

我们沿用上面介绍过的例子,在此基础上使用numba.jitnumba可以将 Python 函数 JIT 编译为机器码执行,大大提高代码运行速度。关于numba的更多信息见下面的主页:http://numba.pydata.org/numba.pydata.org

# 推荐写法。代码耗时:0.62秒
import numba

@numba.jit
def computeSum(size: float) -> int:
    sum = 0
    for i in range(size):
        sum += i
    return sum

def main():
    size = 10000
    for _ in range(size):
        sum = computeSum(size)

main()

8. 选择合适的数据结构

Python 内置的数据结构如str, tuple, list, set, dict底层都是 C 实现的,速度非常快,自己实现新的数据结构想在性能上达到内置的速度几乎是不可能的。

list类似于 C++ 中的std::vector,是一种动态数组。其会预分配一定内存空间,当预分配的内存空间用完,又继续向其中添加元素时,会申请一块更大的内存空间,然后将原有的所有元素都复制过去,之后销毁之前的内存空间,再插入新元素。

删除元素时操作类似,当已使用内存空间比预分配内存空间的一半还少时,会另外申请一块小内存,做一次元素复制,之后销毁原有大内存空间。

因此,如果有频繁的新增、删除操作,新增、删除的元素数量又很多时,list的效率不高。此时,应该考虑使用collections.dequecollections.deque是双端队列,同时具备栈和队列的特性,能够在两端进行 O(1) 复杂度的插入和删除操作。

list的查找操作也非常耗时。当需要在list频繁查找某些元素,或频繁有序访问这些元素时,可以使用bisect维护list对象有序并在其中进行二分查找,提升查找的效率。

另外一个常见需求是查找极小值或极大值,此时可以使用heapq模块将list转化为一个堆,使得获取最小值的时间复杂度是 O(1)

下面的网页给出了常用的 Python 数据结构的各项操作的时间复杂度:https://wiki.python.org/moin/TimeComplexity

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 中 10 个常用的内置函数

在 3.8 版本中,Python 解释器有近 69 个内置函数可供使用,有了它们能极大地提高编码效率

数量虽然不少,但在日常搬砖中只用到其中一部分,根据使用频率和用法,这里列出来几个本人认为不错的内置函数,结合一些例子介绍给大家

complex()

返回一个形如 a+bj 的复数,传入参数分为三种情况:

  • 参数为空时,返回0j
  • 参数为字符串时,将字符串表达式解释为复数形式并返回
  • 参数为两个整数(a,b)时,返回 a+bj
  • 参数只有一个整数 a 时,虚部 b 默认为0,函数返回 a+0j
>>> complex('1+2j')
(1+2j)
>>> complex()
0j
>>> complex(1,2)
(1+2j)
>>> complex(2)
(2+0j)

dir()

  • 不提供参数时,返回当前本地范围内的名称列表
  • 提供一个参数时,返回该对象包含的全部属性
>>> class Test:
 def first(self):
  return 1
 def second(self):
  return 2
>>> dir(Test)# 提供参数时
['__class__''__delattr__''__dict__''__dir__''__doc__''__eq__''__format__''__ge__''__getattribute__''__gt__''__hash__''__init__''__init_subclass__''__le__''__lt__''__module__''__ne__''__new__''__reduce__''__reduce_ex__''__repr__''__setattr__''__sizeof__''__str__''__subclasshook__''__weakref__''first''second']
>>> dir()# 未提供参数时
['Test''__annotations__''__builtins__''__doc__''__loader__''__name__''__package__''__spec__']

divmod(a,b)

  • a — 代表被除数,整数或浮点数;
  • b — 代表除数,整数或浮点数;

根据 除法运算 计算 a,b 之间的商和余数,函数返回一个元组(p,q) ,p 代表商 a//b ,q 代表余数 a%b

>>> divmod(21,4)
(5, 1)
>>> divmod(20.1,3)
(6.0, 2.1000000000000014)

enumerate(iterable,start=0)

  • iterable — 一个可迭代对象,列表、元组序列等
  • start — 计数索引值,默认初始为0

该函数返回枚举对象是个迭代器,利用 next() 方法依次返回元素值,每个元素以元组形式存在,包含一个计数元素(起始为 start )和 iterable 中对应的元素值;

>>> stu = ['xiao','zhang','li','qian']
>>> enumerate(stu)
<enumerate object at 0x0000025331773F48>
>>> list(enumerate(stu))# 默认start = 0
[(0, 'xiao'), (1, 'zhang'), (2, 'li'), (3, 'qian')]
>>> list(enumerate(stu,start = 1))# 默认start = 1
[(1, 'xiao'), (2, 'zhang'), (3, 'li'), (4, 'qian')]

eval(expression,globals,locals)

  • expression — 字符串表达式
  • globals — 变量作用,全局命名空间,如果提供必须为字典形式;可选参数
  • locals — 变量作用域,局部命名空间,如果提须可为任何可映射对象;可选参数

将字符串表达式解析,返回执行结果

>>> eval("2+2")
4
>>> eval("2==2")
True

filter(function,iterable)

  • function — 提供筛选函数名,返回 true 或 false

  • iterable — 列表、元组等可迭代对象

函数返回 iterable 中满足 function 为 True 的元素;假设有一个列表,我们只需要其中的一部分元素,这时就可以提前写一个函数再借助 filter 来进行过滤

>>> num_list = list(range(20))
>>> num_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> def mod_three(num):
 if(num%3==0):
  return True
 else:
  return False

>>> filter(mod_three,num_list)
<filter object at 0x00000253315A6608>
>>> list(filter(mod_three,num_list))
[0, 3, 6, 9, 12, 15, 18]

isinstance(object,classinfo)

  • object –表示一个类型的对象,若不是此类型, 函数恒返回 False;
  • calssinfo — 为一个类型元组或单个类型;

判断对象 object 的类型是否为 classinfo 或 classinfo 中其中一个类型,返回 True 或 False;

调试Python程序时,障碍之一就是初始变量没有定义类型,所以在代码编写或者调试时,isinstance() 函数常被用来判断变量类型,帮助我们了解程序整个逻辑防止出错

>>> isinstance(num,str)
False
>>> isinstance(num,int)
True

map(function,iterable,…)

  • function — 执行函数;
  • iterable — 可迭代对象;

将 iterable 中的每一个元素放入函数 function 中依次执行,得到与 iteable 元素数量相同的迭代器;

map() 中可迭代对象可以为多个,其中函数 function 中的参数为所有可迭代对象中并行元素,最终得到的迭代器的数量是以提供元素数量最少可迭代对象为基准,当数量最少的可迭代对象所有元素输入完毕后即执行停止

>>> def fun_a(a,b):
 return a+2*b

>>> num_list1 = [1,2,3,4]
>>> num_list2 = [2,3,4,5]
>>> list(map(fun_a,num_list1,num_list2))
[5, 8, 11, 14]

input()

函数用于人机交互,读取从输入端输入的一行内容,转化为字符串

>>> s = input('input:')
input:小张Pyhon
>>> s
'小张Pyhon'

zip(*iteables)

  • *iterables — 两个或两个以上可迭代对象

将所有 iterable 对象中元素按参数顺序以元素并行方式聚合在一起,得到一个迭代器,迭代器中每个元素是个n元素元组,n 表示函数中输入 iterable 参数数量

>>> num_list1 = [1,2,3,4]
>>> num_list2 = [2,3,1,2]
>>> list(zip(num_list1,num_list2))
[(1, 2), (2, 3), (3, 1), (4, 2)]

zip() 函数功能等价于

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

除了上面列举的 10 个之外,还有其它 59 个内置函数,想了解的小伙伴可点击下方原文阅读去了解

对于 Python 开发者来说,掌握这些内置函数非常有用,一方面能提高编码效率,另一方面也能提高代码简洁性;在某一方面功能虽然没有第三方库函数强大,但也不能被小觑

好了以上就是本篇文章的全部内容了,最后感谢大家阅读,我们下期见~

本文转自AirPython.

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python Selenium 实现网站自动签到教程

前情提要

小五收藏了一些论坛网站,经常需要自己登录签到,以此来获得积分金币等等。

步骤倒是并不复杂,只需要填写账号密码登录,然后点击签到即可。

但天天手动太容易忘了这件事啦。毕竟我们都会用python了,那就可以使用Selenium操作;浏览器实现自动签到啊!

现在开始上手工作👉

准备工作

首先我们需要先安装Selenium,从而实现后续自动化操控浏览器。我们可以利用它来模拟鼠标按键,跟按键精灵很类似。

pip install selenium

待其安装完成后,准备另一个必需工具。

selenium可以操控多款浏览器,包括谷歌,火狐等,这里小五使用的是谷歌浏览器。

这里需要知道浏览器的版本信息,只需打开“关于Chrome”,就可以看到了。

如上图所示,我的浏览器版本是89.0.4389.114。然后我们需要前往(http://chromedriver.storage.googleapis.com/index.html)找到与浏览器相匹配的版本,没有一模一样的选择最近的版本也可以。

点击进去选择对应的系统版本,下载后将chromedriver.exe解压出来,最后将其放到与python.exe文件相同的路径下。

如果你是用的anaconda就放在下面目录下

C:\Users\Administrator\anaconda3

具体位置如下图所示👇

下面我们就可以正式用python自动签到了。

selenium 教程-代码及讲解

首先打开我要登录的网站,具体域名就不分享给大家了。

先导入selenium库,这里只需使用selenium中的webdriver模块,运行

from selenium import webdriver

打开下载的浏览器驱动,设置隐式等待时

wd=webdriver.Chrome()
wd.implicitly_wait(1)

👆执行代码的时候会自行去寻找chromedriver.exe(在python目录下寻找)。如果我们前面没有把它放在固定的路径下,就需要在这里指定chromedriver.exe路径。

打开登录网页

wd.get('待登录网站URL')

如上图所示,模拟浏览器已经打开了网站的登录界面。这个时候我们需要定位到输入框、密码框以及登录按钮等。

这里不用担心,Selenium提供了很多种定位DOM元素的方法,各有各的特点和优势。今天就主要使用 by_xpath() 这个方法来定位元素,这个方法比较灵活方便,大部分属性都可以通过它来定位。

【检查】→【进入开发者模式】点击左上角的图标,再点击你要找的对象,即可得到该对象的信息。点位该对象后,右键copy它的XPath!

input = wd.find_element_by_xpath('//*[@id="email"]')
input.send_keys('kxpython@163.com')

同理,我们可以定位到密码框,再send_keys输入密码

password = wd.find_element_by_xpath('//*[@id="password"]')
password.send_keys('kxpython')

至于需要点击的对象,可以使用click()来实现模拟点击的功能。

点击登录

button_login = wd.find_element_by_xpath('//*[@id="app"]/section/div/div/div/div[2]/form/div/div[5]/button')
button_login.click()

点击跳过弹窗

一般登录后会有个小弹窗,关闭即可。

wd.find_element_by_xpath('//*[@id="popup-ann-modal"]/div/div/div[3]/button').click()

尝试签到

try:
    wd.find_element_by_xpath('//*[@id="checkin-div"]/a').click()
except:
    print("已签到")

最后要记得关闭浏览器窗口

wd.quit()

注:close()关闭当前窗口,wd.quit()则是关闭所有窗口。

点击运行上面的全部代码,即可实现python对网站的自动签到。我们还可以将该python脚本设置为定时任务,这样就不用每天手动去签到啦!

小结

作为演示的这个网站非常简单,如果大家也想用python来自动签到的话,要根据自己的网站情况来改写脚本。

其实 Selenium 更多的用法是自动化测试、爬虫等,具体Selenium的详细用法,可以参考文档。

地址:http://selenium-python.readthedocs.org/

我们学习python时,可以尝试用来解决自己的生活问题,这样学得也更扎实,学得也更有趣味!

来源:快学Python

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典