小议程序的优雅

date
Jan 21, 2021
slug
小议程序的优雅
status
Published
tags
兴趣
summary
主要还是因为我太菜了。
type
Post
URL
主要还是因为我太菜了。
<!--more-->
事情的缘起是这样的,在学习《Python 编程:从入门到到放弃》这本书中我遇到了一道题:
练习 9-15 创建一个列表或元组,其中包含 10 个数和 5 个字母。从这个列表或元组中随机地选择 4 个数或字母,并打印一条消息,指出只要彩票上是这 4 个数或字母,就中大奖了。可使用循环来搞明白中彩票大奖有多难。为此,创建一个名为 my_ticket 的列表或元组,再编写一个循环,不断地随机选择数或字母,直到中大奖为止。请打印一条消息,指出执行循环多少次才中了大奖。
自己写的代码始终跑不出结果,百思不得其解遂请教 Jam 和 Poka 大佬。
Poka 给出的代码如下:
from random import choice

def random_game():
    possibilities = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "a", "b", "c", "d", "e"]
    tickets = []
    while len(tickets) < 4:
        v = choice(possibilities)
        if v not in tickets:
            tickets.append(v)
    return tickets

winner_tickets = random_game()
print(f"The Winner tickets is {winner_tickets}!")

user_tickets = []
num = 0

while user_tickets != winner_tickets:
    user_tickets = random_game()
    # 如果需要考虑列表中元素的顺序
    # user_tickets.sort()
    # winner_tickets.sort()
    num = num + 1
    print(user_tickets)

print(f"Congratulation! The user tickets is {user_tickets},")
print(f"His odds of winning are 1/{num}!")
Jam 给出的代码如下:
from random import sample
from typing import List, Set

def random_game(possibilities: List, nums: int = 4) -> Set:
    return set(sample(possibilities, nums))

def main():
    possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e"]
    nums = 4

    winner_tickets = random_game(possibilities, nums)
    print(f"The winner tickets is {winner_tickets}.")
    num = 0
    while True:
        user_tickets = random_game(possibilities, 4)
        # print(f"The user tickets is {user_tickets}.")
        num = num + 1
        if user_tickets == winner_tickets:
            print("Congratulations")
            break
    print(num)

if __name__=='__main__':
    main()
两位大佬给出的代码都能很快的计算出结果。再看看我的渣代码:
from random import choice

def random_game():
    tickets = []
    possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e"]
    while len(tickets) < 4:
        value = choice(possibilities)
        if value not in tickets:
            tickets.append(value)
    return tickets

winner_tickets = random_game()
print(f"The winner tickets is {winner_tickets}.")

active = True
num = 0
while True:
    user_tickets = random_game()
    #print(f"The user tickets is {user_tickets}.")
    num = num + 1
    for value in user_tickets:
        if value in winner_tickets:
            break
print(num)
当然我的代码是跑不出结果的。在随后与两位大佬们的交流中,以及对这道题的回顾里我有了一些想法。这道题不能算难题。为什么我没解出来呢,是因为我真的太蔡了,基础不够扎实,还不能很灵活的应用学到的知识。
这道题在于对随机的列表的生成和对比。在 Poka 的解法里,首先使用 choice() 函数在 15 个元素中先挑选 1 个元素通过 append 补充到 winner_tickets 列表中,虽然再重复从这 15 个元素中再抽取一个对象并于已存在与 winner_tickets 中的元素相比对,如果该元素不存在于 winner_tickets 中则 appendwinner_tickets 中。如此处理直到 winner_tickets 中凑满 4 个元素。依旧如此处理 user_tickets 列表。获得两个列表,即为得奖的彩票和用户抽到的彩票,由于题目不要求两个列表需要顺序相同,因此使用 score() 对 user_ticketswinner_tickets中的元素进行排序后相比对。如果排序后两个列表相同,则视为中奖。用 while 设置 user_tickets 生成并于 winner_tickets 比对的循环,并计数。当比对成功后,输入计数和相关结果。
在 Jam 的解法里,引入了模块 typing
Introduced since Python 3.5, Python's typing module attempts to provide a way of hinting types to help static type checkers and linters accurately predict errors.
使用 typing 模块,可使代码有更好的 readibility,也便于查错。在随机挑选元素的过程中,Jam 选择了 sample() 函数:
The sample() method returns a list with a randomly selection of a specified number of items from a sequnce.
sample() 相比于之前使用的 choice() 更加简便,写起来也有着更少的步骤。 sample() 提供了随机采样,得到的结果可能包含重复的元素,但由于在 def 中使用了 -> Set,则 random_game() 得到的是一个集合,不会包含重复元素。
搞定了随机生成接下来就是 user_ticketswinner_tickets 的比较了。由于 winner_ticketswinner_tickets 均是受 set() 处理的集合,因此可以直接比较:
if user_tickets == winner_tickets:
轻松解决。由于在这道题目里面不在意顺序,只在意里面有没有,使用 list 肯定不是最好的选择。
比较 list 通常的两种方法,一种是 for 轮询,另一种就是 Poka 使用的 sort() 先排序后比对。如果使用轮询就需要遍历一次和比较四次才能获取两者是否相同,这并不是个高效的方案。如果 num 更大,则显然需要更多的时间来进行比对。如果使用 sort() 排序,则在时间复杂度上是非常低效的,尤其在 num 巨大、元素复杂时。那么在那么就可以直接采用 set 的方式,用集合来存储数据,可以高效的进行比对。 Jam在这里提到了一个时间复杂度的概念,我目前还没看懂。基本逻辑是,set 在 Python 内部实现是 dict,而 dict 的效率要高于 list
notion image
那么,如何写出优雅的代码?把握代码的性能和运行时间,占用 CPU,以及使用内存,还有可读性、延展性,最后阅读者会感叹一句,“真优雅!”。当然这一切的前提是写了大量的不优雅的代码,看过无数的 sample,和持续的思考。 Jam 使用的方法和提到的概念,是我不曾接触过的。为什么学 Python 呢?我期待将编程和医学结合起来,尝试能不能用编程来对付那些重复劳动,和给我带来更多的东西。对于编程,我什至现在觉得一切技能都是基础,除了思考。长路漫漫。
“The most important thing you'll learn through out the three years is how to use google."
作为一个初学者,我定这篇水文题目《小议程序的优雅》肯定是大了。我目前没有能力来议论什么是优雅的代码,但我庆幸能遇到一些热心的大佬,也很想知道未来的我看到现在的我写下的代码的心情除了“幼稚”和一丝恶心之外还有什么。可能未来我也有能力写一手优雅的代码吧。

Jam 是 Python 开发工程师、安全研究员,现就职于西安某信息安全公司。

© Meow 2020 - 2022