所有由Python实用宝典发布的文章

Python 不可变对象真的不可变吗?

在日常的工作和学习中,经常会遇到“不可变对象 ” 相关的问题,但是随着接触Python这门语言的时间越来越多,遇到的坑越来越奇怪。我不禁产生了一个疑问:不可变对象真的不可变吗?

我们知道元组就是”不可变对象”,当你想尝试给一个元组赋值的时候,它会报错:

>>> my_tuple = ("python", "dict", "is", "good")
>>> my_tuple[3] = "excellent" 
Traceback (most recent call last):   
File "<stdin>", line 1, in <module> 
TypeError: 'tuple' object does not support item assignment 

请注意报错的语言: “does not support item assignment “, 元组对象不支持赋值操作。

例1

让我们来看看下面这样的操作:

>>> int_tuple = ([6,5,30], [2,3,65], [111])
>>> int_tuple[2] += [22,33]
Traceback (most recent call last):  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> int_tuple([6, 5, 30], [2, 3, 65], [111, 22, 33]) 

尽管报了一个错,但是它变量的值已经被改变了。

原因

其实,这里主要是因为 += 操作对于“不可变对象”会产生新的变量并绑定到原有变量上。但是又由于+=中的”=”号,执行了一次对元组对象的赋值操作,这是不允许的,因此报了错,但新的对象已经被绑定到了原有变量中,因此我们可以看到变量的值发生了改变。

例2

还有一种情况是这样的:

>>> int_tuple = ([6,5,30], [2,3,65], [111])
>>> int_tuple[2].append(123123)
>>> int_tuple
([6, 5, 30], [2, 3, 65], [111, 123123]) 

这里完全没有报错,为什么呢?因为append并不涉及到赋值操作,元组只是不允许赋值,并没有不允许append和extend啊,所以称元组为“不可变对象” 实在是太不严谨了!

深一点

其实如果你知道元组中存放的是元素所对应的地址(ID),就好理解多了,append和extend仅仅是修改了列表的元素,而列表本身的ID并没有发生变化,只有当赋值操作执行的时候,ID才会发生变化,而这种情况是元组不允许发生的。

# append 不会发生地址变化
>>> int_tuple = ([1], [2], [3])
>>> id(int_tuple[1])
2729947990600
>>> int_tuple[1].append(22)
>>> id(int_tuple[1])
2729947990600 

因此,要避免踩上这些坑,忘掉元组是“不可变对象”这样不严谨的表述形式(当然从地址的角度来讲它是对的,元组内部所有元素的ID都不可变),请这样记:元组是不可赋值对象

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 9行代码批量翻转壁纸的惊喜

今天在欣赏一些新的手机壁纸的时候,不小心把手机拿反了,但是却因此偶然看到了非常有特色的图片。因此突然有了推送的灵感——用Python批量翻转图片

其实原理很简单,用九行代码就能完成我们所需要的功能。

1.准备

为了能够翻转图片,我们需要先安装好PIL,也就是pillow,在CMD/Terminal输入:

pip install pillow

请记得先安装Python,才能够pip,如果你还没有安装Python,请看这篇文章: 超详细Python安装指南

2.编程

仅需要3个步骤:

1.获得图片文件夹下所有文件名

2.迭代所有图片

3.翻转保存到指定文件夹

#-*- coding: UTF-8 -*- 

from PIL import Image
import os

# 获得文件夹下所有文件
filePath = './imgs/'
filenames = os.listdir(filePath)

# 指定保存的文件夹
outputPath = './imgs_rotate/'

# 迭代所有图片
for filename in filenames:
    # 读取图像
    im = Image.open(filePath + filename)

    # 指定逆时针旋转的角度
    im_rotate = im.rotate(180)
    
    # 保存图像
    im_rotate.save(outputPath + filename) 

3.效果

嘻嘻,其实也是相当于壁纸分享了,当然,微信公众号会有压缩的现象,大家如果想下载原图请点击下方阅读原文,或者关注文章最下方Python实用宝典公众号,回复 翻转壁纸

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 小坑之字符串驻留

本文整理了许多字符串驻留的坑,部分整合自wtfpython英文版,并增加了大量的后续说明。

# example1:
>>> a = "wtf"
>>> b = "wtf"
>>> a is b
True

# example2:
>>> a = "wtf!"
>>> b = "wtf!"
>>> a is b
False

# example3:
>>> a, b = "wtf!", "wtf!"
>>> a is b 
True # 3.7 版本返回结果为 False. 
# example4:
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False # 3.7 版本返回结果为 True  

字符串的这些问题,像是在和你说 1 != 1 一样坑爹。

究其原因,其实是CPython在编译的时候会自动进行优化,在某些情况下它会尝试使用已经存在的不可变对象,而不是创建一个新的对象,而恰好,字符串就是不可变对象。这种使用已存在的不可变对象的行为被称为“驻留 ” 。

驻留的原本设计意图是用于节省内存的,但是确实有时候会坑到程序员。怎样判断自己的字符串会否被驻留呢?请看这份代码:
https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19

简单地来讲:

1.所有长度为0和1的字符串都会被驻留

2.字符串在编译时被实现的会被驻留(如’wtf’会被驻留,但是 ”.join([‘w’, ‘t’, ‘f’]) 不会)

3.字符串中只包含ASCII下的字母、数字和下划线时会被驻留. 所以’wtf!’由于包含!不会被驻留。

我们的example1中,由于发生了驻留,所以a和b是同一个字符串对象。而example2中,由于没有发生字符串驻留,a=”wtf!”和b=”wtf!”实际上使用的不是同一个字符串对象,你可以使用id获得对象的唯一标志,你会发现它们的不同:

a和b都为wtf!时:

>>> a = "wtf!"
>>> b = "wtf!"
>>> a is b
False
>>> a == b
True
>>> id(a)
2272774097864
>>> id(b)
2272774097024  

再来看看没有发生驻留时的情况,a和b都为wtf时:

# a和b都为wtf
>>> a = "wtf"
>>> b = "wtf"
>>> a is b
True
>>> a == b
True
>>> id(a)
2272774096744
>>> id(b)
2272774096744 

明白了吧?如果你想从结果识别对象是否发生驻留,关键就看对象的唯一标志有没有被改变。

不过,如example3所示,当你在同一行中将a和b都设置为 wtf! 的时候,Python解释器会创建一个新的对象,然后同时引用第二个变量,这时候它两的唯一标志id就是一样的。(example3仅适用于python3.7以下,后面被改了)。

example4中,发生了常量折叠,这其实也是一种优化技术。编译时表达式 ‘a’*20 会被替换成 ‘aaaaaaaaaaaaaaaaaaaa’ (不要数了,20个),不过只有长度小于20的字符串才会发生常量替换,这就是为什么 ‘a’*21并不等于 ‘aaaaaaaaaaaaaaaaaaaaa’ (不要数了,21个) 。

好,感谢大家的阅读,今天的…..等等,你以为这就结束了吗?还有呢:

>>> a = 10
>>> b = 10
>>> a is b
True
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False 

这又是为啥啊?请注意,Python中,对于整数对象,如果其值处于[-5,256]的闭区间内,则值相同的对象是同一个对象,否则为不同对象。我知道你想问,别问,问就是源码本身就这么写的(其实主要还是从性能方面考虑,-5到256这段数值被经常使用,因此干脆设为同一个对象重复使用,避免分配空间—赋予类别—赋予初始值等一系列操作)。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

2020年已到,Python2寿命已止,请迁移

随着2020年的到来,Python 2 也到来寿终正寝的时候。从2020年1月1日起,Python核心团队将不再对Python 2提供任何支持。

1.怎么办?

由于Python3不向前兼容Python2,因此对于有写过Python2,且有正在运行Python2项目的同学来说,是时候将项目迁移到Python3了。

对于正在学习Python的同学来说,不要犹豫,请直接学Python3的教程,如果某个你正在阅读的教程是Python2写的,请立即丢掉。

2.迁移方法

现在已经有许多自动化的工具可以帮你把代码从Python 2迁移到Python 3. 比如说自带的2to3模块:

2to3 的基本调用参数是一个需要转换的文件或目录列表。对于目录,会递归地寻找其中的 Python 源码。

这里有一个 Python 2.x 的源码文件,example.py

def greet(name):
    print "Hello, {0}!".format(name)
print "What's your name?"
name = raw_input()
greet(name)

找到你的Python安装位置,一般2to3会在Tools\scripts文件夹下:C:\Python3X\Tools\scripts\2to3.py,在CMD中输入以下命令即可将Python2代码转化为Python3代码(由于我用的是anaconda,所以位置可能和你们不一样):

F:\Anaconda3\Scripts\2to3.exe example.py

它会显示所有修改,输出效果如下:

 RefactoringTool: Skipping optional fixer: buffer
 RefactoringTool: Skipping optional fixer: idioms
 RefactoringTool: Skipping optional fixer: set_literal
 RefactoringTool: Skipping optional fixer: ws_comma
 RefactoringTool: Refactored example.py
 --- example.py  (original)
 +++ example.py  (refactored)
 @@ -1,5 +1,5 @@
  def greet(name):
 print "Hello, {0}!".format(name)
 -print "What's your name?"
 -name = raw_input()
 print("Hello, {0}!".format(name))
 +print("What's your name?")
 +name = input()
 greet(name)
 RefactoringTool: Files that need to be modified:
 RefactoringTool: example.py 

如果你想写回文件,记得带-w参数:

F:\Anaconda3\Scripts\2to3.exe -w example.py

现在效果如下:

def greet(name):
    print("Hello, {0}!".format(name))
print("What's your name?")
name = input()
greet(name) 

非常方便,一键修改完成。它能转化大部分Python2代码,更详细的使用文档在这里:

https://docs.python.org/zh-cn/3.7/library/2to3.html#

不过2to3.py也不是万能的,有些情况下你只能手动地转换代码,遇到无法用工具迁移的问题,请耐心搜索解决方案

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 重载与重写 及 泛型函数

在上一次的推送《Python 监控文件事件变化—以音乐高潮提取为例》中,最后继承 LoggingEventHandler 类,对 on_created 进行修改的时候,我使用了一个词叫:重载,随后不久我便意识到犯下了一个错误。

我们先来看看重载与重写的概念:

重载:

重载的条件如下:

1.一个类里面

2.方法名字相同

3.参数不同

而参数不同可分为:参数类型不同,参数个数不同。而上一次推送中,对on_created的修改并没有针对参数,我们只是单纯地对函数内容进行修改而已。因此只能叫重写

重写:

条件如下:

1.参数列表与原函数一致。

2.返回类型与原函数一致。

因此,根据这个条件,Python中绝大部分的继承修改只能叫重写,而不能叫重载

Python其实不需要重载这个概念。为什么呢?重载主要是针对参数而言的,一个是改变参数的类型,一个是改变参数的个数。而Python不需要限定参数类型,又 可以接受可变参数,因此函数重载就显得非常鸡肋了。

不过我们依然能通过singledispatch装饰器来实现重载,不过官方称其为泛型函数:

在需要被重载的函数上加上装饰器:

from functools import singledispatch


@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg) 

然后用register属性注册重载后的函数:

@fun.register(int)
def _(arg: int, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)


@fun.register(list)
def _(arg: list, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem) 

效果如下:

>>> fun(42, verbose=True)
Strength in numbers, eh? 42

>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam 

看,这样我们就顺利完成了重载,尽管其实大部分时候都不会用到。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 监控文件增加、修改、删除等变化

现在有一个应用场景,需要对文件系统进行监控,发生变化时产生日志,对新增的文件做一些相应的操作。比如说应用到我们之前的高潮提取器:若当前文件夹下增加了一个音乐文件,监控器就调用高潮提取器提取该音乐文件的高潮部分。

这样的监控器写起来也不难,但是很花时间,有许多情况要考虑。不过幸好我们是写Python的,有许多轮子可以使用。

1.安装”看门狗”

“看门狗”模块就是用于监控文件事件变化的一个Python”轮子”,代码架构优秀,可以注册许多事件处理器,方便用户做自定义操作。如果你还没有安装Python,请看这篇文章《Python详细安装指南》,在终端输入以下命令即可安装看门狗:

pip install watchdog

2.基本使用

看门狗的使用其实不复杂,请认真看以下代码和注释:

import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = sys.argv[1] if len(sys.argv) > 1 else '.'

    # 生成事件处理器对象
    event_handler = LoggingEventHandler()

    # 生成监控器对象
    observer = Observer()
    # 注册事件处理器,配置监控目录
    observer.schedule(event_handler, path, recursive=True)
    # 监控器启动——创建线程
    observer.start()

    # 以下代码是为了保持主线程运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    # 主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止
    observer.join()

可以看到代码中有几个关键步骤,1.配置各项信息;2.生成事件处理器、监控器;3.注册事件处理器、配置目录、递归执行(即同时监控子文件夹);4.启动。

其实,看门狗的observer是基于 threading.Thread 对象的,所以observer很多属性都继承了 threading.Thread 的属性。

如果你不带参数地运行该脚本,就是要监控脚本文件所在的文件夹,如果要监控其他文件夹,记得运行时带文件夹的路径参数,如:

python obserber.py /data/home/ckend/

我们来试着运行看看:

可以看到,我在当前文件夹下做的所有操作都被记录下来了。接下来我们就试试怎么自定义一些操作。

3.监控提取音乐高潮

如果你不知道怎么提取音乐文件的高潮部分,请看这篇文章:《Python自动提取音乐文件高潮》

要实现这样的功能,我们有几种方法,一个是在原来log的处理器上做一些新增修改,比如多增一个函数调用音乐高潮提取器。第二个是重新继承 FileSystemEventHandler 类,并做相应的修改。这里我们还是要保留log的样式,只是在log的时候顺便提取音乐高潮,因此采用第一个方法。

看看LoggingEventHandler源代码中的on_created,这就是当文件创建时监控器的操作:

class LoggingEventHandler(FileSystemEventHandler):
    """Logs all the events captured."""

    # ...省略其他源代码...

    def on_created(self, event):
        super(LoggingEventHandler, self).on_created(event)

        what = 'directory' if event.is_directory else 'file'
        logging.info("Created %s: %s", what, event.src_path)

我们仅需要继承这个类并对on_created进行修改,就能完成我们想要的功能:

# Python实用宝典
# 2019/12/29

import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler
from pychorus import find_and_output_chorus


class extractor(LoggingEventHandler):

    def on_created(self, event):
        super(LoggingEventHandler, self).on_created(event)
        what = 'directory' if event.is_directory else 'file'
        logging.info("Created %s: %s", what, event.src_path)
        NameExt = event.src_path.split('.')
        if NameExt[-1] == 'mp3':
            logging.info("mp3文件, 提取音乐高潮中...")
            output_path = "."+"".join(NameExt[:-1])+'_high.wav'
            find_and_output_chorus(event.src_path, output_path, 30)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = sys.argv[1] if len(sys.argv) > 1 else '.'

    # 生成事件处理器对象
    event_handler = extractor()

    # 生成监控器对象
    observer = Observer()
    # 注册事件处理器
    observer.schedule(event_handler, path, recursive=True)
    # 监控器启动——创建线程
    observer.start()

    # 以下代码是为了保持主线程运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    # 主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止
    observer.join() 

首先声明一个类,继承LoggingEventHandler,然后重载on_created函数,在这个函数中不仅记录文件事件变化,还要对mp3文件做一次音乐高潮提取。最后别忘了,生成事件处理器时要用我们新的类名。

看看效果,将小永远.mp3复制过来:

成功监控文件变化并提取到音乐高潮,生成高潮文件。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

庆祝胖五发射成功, 来用Python发射火箭!

今天看到了一则可喜可贺的消息:长征五号遥三运载火箭在中国文昌航天发射场点火升空,与实践二十号卫星成功分离,任务取得圆满成功。 真是令人自豪,为这些辛苦工作的航天人喝彩。

不过,火箭发射似乎离我们普通人太远了,如果我们想体验一把亲自将火箭送上天的感觉怎么办呢?好像很难啊?没关系,Python有方法,现实世界里做不到,但是你可以在虚拟世界里实现。

《坎巴拉太空计划》就是一个能让你实现这个愿望的一款游戏。在这个游戏中,玩家拥有一支庞大的航天团队,能够造出你想要的任意航天器,你也可以驾驶航天器在坎巴拉星系中遨游,建立太空站。

更重要的是,它!支持!Python!

你可以使用Python来画你想要的航天器、设置警报、航天器之间的通讯、控制运行轨道、监控燃料量等等一系列功能,甚至能够改变游戏界面。

在Space Center API中,你可以读取作用在飞船上的重力、获得海拔高度、绕行轨道的纬度、参考系速度、控制游戏内部相机等等,几乎一切想读取的它都可以读取到。

1.安装

你可以通过pip安装这个项目:

pip install krpc

如果你还没有安装python,请看这篇文章:安装Python

注意,使用这个项目的前提是先下载好游戏。而且在运行脚本的时候,游戏必须在运行着,并与客户端保持连接。怎样才能和客户端连接呢?你还需要下载kRPC服务器插件:

https://github.com/krpc/krpc/releases/download/v0.4.8/krpc-0.4.8.zip

然后执行以下操作:

  1. 提取gamedata文件夹到您的KSP目录。
  2. 启动游戏。
  3. 这时候应该就会弹出服务器窗口

2.使用

运行游戏,并且服务器正常启动后,我们就可以开始尝试一些例子了。不过在这之前,Python脚本作为客户端还需要和服务器进行连接:

import krpc
conn = krpc.connect(name='Hello World')
vessel = conn.space_center.active_vessel
print(vessel.name) 

第二行连接服务器,第三行获得激活的飞行器、第四行打印飞行器名字:

你还可以尝试一些别的API,比如下面这个例子,能够获得飞行棋相对于参照物(行星)的速度:

import krpc, time
conn = krpc.connect(name='Surface speed')
vessel = conn.space_center.active_vessel

while True:

    velocity = vessel.flight(vessel.orbit.body.reference_frame).velocity
    print('Surface velocity = (%.1f, %.1f, %.1f)' % velocity)

    speed = vessel.flight(vessel.orbit.body.reference_frame).speed
    print('Surface speed = %.1f m/s' % speed)

    time.sleep(1) 

如果你们感兴趣,推荐先下载游戏并试玩,大致摸清楚游戏的玩法后,开始使用Python来进行编程游戏。说实话,这个游戏太适合用来教小孩子了。

Python和游戏之间详细的API都在这里可以找得到:

http://krpc.github.io/krpc/python.html

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 警惕伪装成著名软件包的恶意模块

本周早些时候,两个包含恶意代码的Python第三方模块被从官方软件PyPI中删除。这两个恶意软件包通过使用非常相似的名字来迷惑安装者。

它们伪装成dateutiljellyfish软件包 (前者用于处理日期时间,后者用于字符串近似计算等),伪装命名为python-dateutiljeIlyfish,如果安装包的时候并不熟悉这些模块,而且也没有看官方文档,那就非常有可能中招。

看,甚至有人不知道它是冒牌货

安装后, python-dateutil 和 jeIlyfish 与原始的软件包行为完全相同,但是它们会偷偷地将个人数据上传到服务器。但是正常使用的人恐怕无法发现恶意模块的这些行为。

Python通常分为两种,一种是Python运行时自带的标准,还有一种则是托管在PyPI上的第三方程序包,而上传第三方程序包到PyPI时,大部分程序包都不会被审核到。这就导致你进行pip安装的时候,如果不小心输错了单词,就有可能安装到恶意模块。

这引出了一个现在许多开软社区都非常头痛的问题:如何让人们能将自己的代码贡献到通用存储(如PyPI),且不被不怀好意的人利用。

据说,Python软件基金会已经制定了保护PyPI免受滥用的计划,但要全面执行计划需要一些时间。此外,Python软件基金会中负责封装的工作组已经获得了Facebook Research的拨款,用于开发自动检测恶意上传第三方程序包的功能。

尽管如此,这些预防措施离上线还有很远,我们在选择使用第三方模块的时候一定要注意你的命令pip install的是不是那个真的模块,而不是一个冒牌货,这样才能将风险降至最低。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

Python 30秒就能学会的漂亮短代码(译2)

上上期,我们给大家带来了30秒就能学会漂亮短代码的第一部分,这些代码其实非常有价值,蕴含了许多python编程思维,应用领域非常广泛,而且学起来非常简单。今天给大家带来的是第二期内容。

1.”二维列表”

解读:根据给定的长和宽,以及初始值,返回一个二维列表。

def initialize_2d_list(w, h, val=None):
    return [[val for x in range(w)] for y in range(h)] 

例:

>>> initialize_2d_list(2,2)
[[None, None], [None, None]]

>>> initialize_2d_list(2,2,0)
[[0, 0], [0, 0]]       

2.函数切割数组

解读:使用一个函数应用到一个数组的每个元素上,使得这个数组被切割成两个部分。如果说,函数应用到元素上返回的值为True,则该元素被切割到第一部分,否则分为第二部分。

def bifurcate_by(lst, fn):
    return [
      [x for x in lst if fn(x)],
      [x for x in lst if not fn(x)]
    ] 

例:

>>> bifurcate_by(['beep', 'boop', 'foo', 'bar'], lambda x: x[0] == 'b')    
[['beep', 'boop', 'bar'], ['foo']]       

3.”交集点”

解读: 两个数组在被一个函数应用后,从第一个数组中提取出共有的元素的原元素组成一个新的数组。

def intersection_by(a, b, fn):
    _b = set(map(fn, b))
    return [item for item in a if fn(item) in _b] 

例:

>>> from math import floor
>>> intersection_by([2.1, 1.2], [2.3, 3.4],floor)
[2.1] 

4.最大值下标

解读:返回数组中最大值的下标。

def max_element_index(arr):
    return arr.index(max(arr)) 

例:

>>> max_element_index([5, 8, 9, 7, 10, 3, 0])
4 

5.数组对称差

解读:找出两个数组中不同的元素,并合成为一个新的数组。

def symmetric_difference(a, b):
    _a, _b = set(a), set(b)
    return [item for item in a if item not in _b] + [item for item in b if item not in _a] 

例:

>>> symmetric_difference([1, 2, 3], [1, 2, 4])
[3, 4] 

原文: https://github.com/30-seconds/30-seconds-of-python

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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

超方便的 Python 唤醒窗口自动截图脚本

利用Python自带的win32api和win32con、win32gui等模块,我们能执行许多windows下的自动化操作。比如两个窗口的自动点击操作,从软件中的窗口复制文本到txt中,甚至是截图操作。

截图的操作用途最为广泛,你可以用它配合定时工具,定时检测某个程序的运行情况;甚至可以根据截图做一些辅助性的决策,比如玩类似于《连连看》的游戏时,对相同类型的方块进行标记,辅助你玩游戏。

下面就讲讲如何使用 win32api 实现自动唤醒并截图的操作。

1.准备

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

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

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

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

pip install pypiwin32
pip install pillow

1.获取窗口左上角及右下角坐标

​通过 win32gui 的 FindWindow 函数,我们能轻易地找到任何进程的窗口:

import win32api, win32con, win32gui
def get_window_pos(name):
    name = name
    handle = win32gui.FindWindow(0, name)
    # 获取窗口句柄
    if handle == 0:
        return None
    else:
        return win32gui.GetWindowRect(handle)
x1, y1, x2, y2 = get_window_pos('暴雪战网')
print(x1,y1,x2,y2)

结果:

F:\push\20190929>python 1.py
(349, 83, 1549, 1013)

其中窗口信息(x1, y1, x2, y2),(x1, y1)是窗口左上角的坐标,(x2, y2)是窗口右下角的坐标。我们可以利用这个信息配合PIL进行截图。但是在这之前,我们还要解决两个问题:

  1. 该窗口并不在当前的界面上,被其他的软件覆盖到底层中,这时候需要高亮窗口
  2. 该窗口被最小化怎么办

2.win32gui 高亮窗口

为了使得被叠在底层的窗口能放到最上层显示,我们需要拿到窗口的handle,对其执行高亮操作,其实很简单,我们刚刚获得坐标信息的时候已经得到handle了,只需要做一下简单的更改即可。

import win32api, win32con, win32gui
def get_window_pos(name):
    name = name
    handle = win32gui.FindWindow(0, name)
    # 获取窗口句柄
    if handle == 0:
        return None
    else:
        # 返回坐标值和handle
        return win32gui.GetWindowRect(handle), handle
(x1, y1, x2, y2), handle = get_window_pos('暴雪战网')
text = win32gui.SetForegroundWindow(handle)

这样就能将被覆盖到底层的窗口放到最上层,如下图所示。

python 高亮窗口

3. 还原最小化窗口

还有一种特殊情况就是窗口被缩小了,这时候我们就需要还原最小化窗口,其实也非常简单,只要利用win32gui和win32con向该窗口发送一个信息即可。

import win32api, win32con, win32gui
def get_window_pos(name):
    name = name
    handle = win32gui.FindWindow(0, name)
    # 获取窗口句柄
    if handle == 0:
        return None
    else:
        # 返回坐标值和handle
        return win32gui.GetWindowRect(handle), handle
(x1, y1, x2, y2), handle = get_window_pos('暴雪战网')
win32gui.SendMessage(handle, win32con.WM_SYSCOMMAND, win32con.SC_RESTORE, 0)
# 发送还原最小化窗口的信息
win32gui.SetForegroundWindow(handle)
# 设为高亮 

效果如图所示:

python还原最小化窗口

4.截图

有了PIL模块和窗口的坐标后,我们想截图可非常简单。PIL 模块安装

pip install pillow

安装完就可以试一下我们的完整代码了,如下:

import win32api, win32con, win32gui

def get_window_pos(name):
    name = name
    handle = win32gui.FindWindow(0, name)
    # 获取窗口句柄
    if handle == 0:
        return None
    else:
        # 返回坐标值和handle
        return win32gui.GetWindowRect(handle), handle
(x1, y1, x2, y2), handle = get_window_pos('暴雪战网')
win32gui.SendMessage(handle, win32con.WM_SYSCOMMAND, win32con.SC_RESTORE, 0)
# 发送还原最小化窗口的信息
win32gui.SetForegroundWindow(handle)
# 设为高亮
from PIL import Image, ImageGrab
img_ready = ImageGrab.grab((x1, y1, x2, y2))
# 截图
img_ready.show()
# 展示 

效果如下:

python 截图

这个功能可好用了,比如说你需要监控一个窗口的运行状况,不可能时时刻刻都去观察它,你可以使用while循环不断调用这个窗口截图脚本,先截图进行保存。

你甚至可以配合定时任务做截图,见我们上篇文章:Schedule—简单实用的 Python 周期任务调度工具

截图后的图表信息还能够用于分析、辅助决策。举个游戏的例子:当你玩《连连看》的时候,可以截图检测每个方块是否相同,把相同的方块标记出来,提高你的连连看游戏效率。

我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


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