Python Skills


90条写Python程序的建议

1. 首先

建议1、理解 Pythonic 概念—-详见 Python 中的《Python之禅》

建议2、编写 Pythonic 代码

(1)避免不规范代码,比如只用大小写区分变量、使用容易混淆的变量名、害怕过长变量名等。有时候长的变量名会使代码更加具有可读性。

(2)深入学习 Python 相关知识,比如语言特性、库特性等,比如Python演变过程等。深入学习一两个业内公认的 Pythonic 的代码库,比如Flask等。

建议3:理解 Python 与 C 的不同之处,比如缩进与 {},单引号双引号,三元操作符?, Switch-Case 语句等。

建议4:在代码中适当添加注释

建议5:适当添加空行使代码布局更加合理

建议6:编写函数的 4 个原则

(1)函数设计要尽量短小,嵌套层次不宜过深

(2)函数声明应该做到合理、简单、易用

(3)函数参数设计应该考虑向下兼容

(4)一个函数只做一件事,尽量保证函数粒度的一致性

建议7:将常量集中在一个文件,且常量名尽量使用全大写字母

2. 编程惯用法

建议8:利用 assert 语句来发现问题,但要注意,断言 assert 会影响效率

建议9:数据交换值时不推荐使用临时变量,而是直接 a, b = b, a

建议10:充分利用惰性计算(Lazy evaluation)的特性,从而避免不必要的计算

建议11:理解枚举替代实现的缺陷(最新版 Python 中已经加入了枚举特性)

建议12:不推荐使用 type 来进行类型检查,因为有些时候 type 的结果并不一定可靠。如果有需求,建议使用 isinstance 函数来代替

建议13:尽量将变量转化为浮点类型后再做除法(Python3 以后不用考虑)

建议14:警惕eval()函数的安全漏洞,有点类似于 SQL 注入

建议15:使用 enumerate() 同时获取序列迭代的索引和值

建议16:分清 == 和 is 的适用场景,特别是在比较字符串等不可变类型变量时(详见评论)

建议17:尽量使用 Unicode。在 Python2 中编码是很让人头痛的一件事,但 Python3 就不用过多考虑了

建议18:构建合理的包层次来管理 Module

3. 基础用法

建议19:有节制的使用 from…import 语句,防止污染命名空间

建议20:优先使用 absolute import 来导入模块(Python3中已经移除了relative import)

建议21:i+=1 不等于 ++i,在 Python 中,++i 前边的加号仅表示正,不表示操作

建议22:习惯使用 with 自动关闭资源,特别是在文件读写中

建议23:使用 else 子句简化循环(异常处理)

建议24:遵循异常处理的几点基本原则

(1)注意异常的粒度,try 块中尽量少写代码

(2)谨慎使用单独的 except 语句,或 except Exception 语句,而是定位到具体异常

(3)注意异常捕获的顺序,在合适的层次处理异常

(4)使用更加友好的异常信息,遵守异常参数的规范

建议25:避免 finally 中可能发生的陷阱

建议26:深入理解 None,正确判断对象是否为空。

建议27:连接字符串应优先使用 join 函数,而不是+操作

建议28:格式化字符串时尽量使用 format 函数,而不是 % 形式

建议29:区别对待可变对象和不可变对象,特别是作为函数参数时

建议30:[], {}和():一致的容器初始化形式。使用列表解析可以使代码更清晰,同时效率更高

建议31:函数传参数,既不是传值也不是传引用,而是传对象或者说对象的引用

建议32:警惕默认参数潜在的问题,特别是当默认参数为可变对象时

建议33:函数中慎用变长参数 args 和 kargs

(1)这种使用太灵活,从而使得函数签名不够清晰,可读性较差

(2)如果因为函数参数过多而是用变长参数简化函数定义,那么一般该函数可以重构

建议34:深入理解 str()和 repr() 的区别

(1)两者之间的目标不同:str 主要面向客户,其目的是可读性,返回形式为用户友好性和可读性都比较高的字符串形式;而 repr 是面向 Python 解释器或者说Python开发人员,其目的是准确性,其返回值表示 Python 解释器内部的定义

(2)在解释器中直接输入变量,默认调用repr函数,而print(var)默认调用str函数

(3)repr函数的返回值一般可以用eval函数来还原对象

(4)两者分别调用对象的内建函数 str ()和 repr ()

建议35:分清静态方法 staticmethod 和类方法 classmethod 的使用场景

4. 库的使用

建议36:掌握字符串的基本用法

建议37:按需选择 sort() 和 sorted() 函数

​ sort() 是列表在就地进行排序,所以不能排序元组等不可变类型。

​ sorted() 可以排序任意的可迭代类型,同时不改变原变量本身。

建议38:使用copy模块深拷贝对象,区分浅拷贝(shallow copy)和深拷贝(deep copy)

建议39:使用 Counter 进行计数统计,Counter 是字典类的子类,在 collections 模块中

建议40:深入掌握 ConfigParse

建议41:使用 argparse 模块处理命令行参数

建议42:使用 pandas 处理大型 CSV 文件

​ Python 本身提供一个CSV文件处理模块,并提供reader、writer等函数。

​ Pandas 可提供分块、合并处理等,适用于数据量大的情况,且对二维数据操作更方便。

建议43:使用 ElementTree解析XML

建议44:理解模块 pickle 的优劣

​ 优势:接口简单、各平台通用、支持的数据类型广泛、扩展性强

​ 劣势:不保证数据操作的原子性、存在安全问题、不同语言之间不兼容

建议45:序列化的另一个选择 JSON 模块:load 和 dump 操作

建议46:使用 traceback 获取栈信息

建议47:使用 logging 记录日志信息

建议48:使用 threading 模块编写多线程程序

建议49:使用 Queue 模块使多线程编程更安全

5. 设计模式

建议50:利用模块实现单例模式

建议51:用 mixin 模式让程序更加灵活

建议52:用发布-订阅模式实现松耦合

建议53:用状态模式美化代码

6. 内部机制

建议54:理解 build-in 对象

建议55: init ()不是构造方法,理解 new ()与它之间的区别

建议56:理解变量的查找机制,即作用域

​ 局部作用域

​ 全局作用域

​ 嵌套作用域

​ 内置作用域

建议57:为什么需要self参数

建议58:理解 MRO(方法解析顺序)与多继承

建议59:理解描述符机制

建议60:区别 getattr ()与 getattribute ()方法之间的区别

建议61:使用更安全的 property

建议62:掌握元类 metaclass

建议63:熟悉 Python 对象协议

建议64:利用操作符重载实现中缀语法

建议65:熟悉 Python 的迭代器协议

建议66:熟悉 Python 的生成器

建议67:基于生成器的协程和 greenlet,理解协程、多线程、多进程之间的区别

建议68:理解 GIL 的局限性

建议69:对象的管理和垃圾回收

7. 使用工具辅助项目开发

建议70:从 PyPI 安装第三方包

建议71:使用 pip 和 yolk 安装、管理包

建议72:做 paster 创建包

建议73:理解单元测试的概念

建议74:为包编写单元测试

建议75:利用测试驱动开发(TDD)提高代码的可测性

建议76:使用 Pylint 检查代码风格

​ 代码风格审查

​ 代码错误检查

​ 发现重复以及不合理的代码,方便重构

​ 高度的可配置化和可定制化

​ 支持各种 IDE 和编辑器的集成

​ 能够基于 Python 代码生成 UML 图

​ 能够与 Jenkins 等持续集成工具相结合,支持自动代码审查

建议77:进行高效的代码审查

建议78:将包发布到 PyPI

8. 性能剖析与优化

建议79:了解代码优化的基本原则

建议80:借助性能优化工具

建议81:利用 cProfile 定位性能瓶颈

建议82:使用 memory_profiler 和 objgraph 剖析内存使用

建议83:努力降低算法复杂度

建议84:掌握循环优化的基本技巧

​ 减少循环内部的计算

​ 将显式循环改为隐式循环,当然这会牺牲代码的可读性

​ 在循环中尽量引用局部变量

​ 关注内层嵌套循环

建议85:使用生成器提高效率

建议86:使用不同的数据结构优化性能

建议87:充分利用 set 的优势

建议88:使用 multiprocessing 模块克服 GIL 缺陷

建议89:使用线程池提高效率

建议90:使用 Cythonb 编写扩展模块

14种漂亮短代码实现

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]

6.”夹数”

解读:如果 num 落在一段数字范围内,则返回num,否则返回离这个范围最近的边界:

def clamp_number(num,a,b):
    return max(min(num, max(a,b)),min(a,b))

例:

>> clamp_number(2,3,10)
3

>> clamp_number(7,3,10)
7

>> clamp_number(124,3,10)
10

7.键值映射

解读:使用对象的键重新创建对象,并运行函数为每个对象的键创建值。
使用dict.keys()遍历对象的键, 通过函数生成一个新的值。

def map_values(obj, fn):
    ret = {}
    for key in obj.keys():
        ret[key] = fn(obj[key])
    return ret

例:

>>> users = {
...   'fred': { 'user': 'fred', 'age': 40 },
...   'pebbles': { 'user': 'pebbles', 'age': 1 }
... }

>>> map_values(users, lambda u : u['age'])
{'fred': 40, 'pebbles': 1}

>>> map_values(users, lambda u : u['age']+1)
{'fred': 41, 'pebbles': 2}

8.大小写转换

解读: 将英文单词的首字母大写改为小写。upper_rest参数:设定是否将除首字母外的其他字母大小写转换。

def decapitalize(s, upper_rest=False):
    return s[:1].lower() + (s[1:].upper() if upper_rest else s[1:])

例:

>>> decapitalize('FooBar')
'fooBar'

>>> decapitalize('FooBar', True)
'fOOBAR'

9.同键求和

解读:对列表中的各个字典里相同键值的对象求和。

def sum_by(lst, fn):
    return sum(map(fn,lst))

例:

>>> sum_by([{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }], lambda v : v['n'])
14

10.一行代码求出现次数

解读:求出列表中某个数出现的次数和。

def count_occurrences(lst, val):
    return len([x for x in lst if x == val and type(x) == type(val)])

例:

>>> count_occurrences([1, 1, 2, 1, 2, 3], 1)
3

11.数组再分组

对一个列表根据所需要的大小进行细分:

效果如下:

chunk([1,2,3,4,5],2)
# [[1,2],[3,4],5]

return中,map的第二个参数是一个列表,map会将列表中的每一个元素用于调用第一个参数的 function 函数,返回包含每次 function 函数返回值的新列表。

12.数字转数组

同样是一则关于map的应用,将整形数字拆分到数组中:

def digitize(n):
    return list(map(int, str(n)))

效果如下:

digitize(123)
# [1, 2, 3]

它将整形数字n转化为字符串后,还自动对该字符串进行了序列化分割,最后将元素应用到map的第一个参数中,转化为整形后返回。

13.非递归斐波那契

还记得菲波那切数列吗,前两个数的和为第三个数的值,如0、1、1、2、3、5、8、13….

如果使用递归来实现这个算法,效率非常低下,我们使用非递归的方式实现:

效果如下:

fibonacci(7)
# [0, 1, 1, 2, 3, 5, 8, 13]

这样看是很简单,但是思维要绕的过来哦。

14.下划线化字符串

批量统一变量名称或者字符串格式。

效果如下:

snake('camelCase')# 'camel_case'

snake('some text')# 'some_text'

snake('some-mixed_string With spaces_underscores-and-hyphens')# 'some_mixed_string_with_spaces_underscores_and_hyphens'

snake('AllThe-small Things')# "all_the_small_things"

re.sub用于替换字符串中的匹配项。这里其实是一个“套娃”用法,一开始可能不太好理解,需要慢慢理解。

第一个替换,是将s字符串中,使用’ ‘替换’-‘。

第二个替换,是针对第一个替换后的字符串,对符合’([A-Z]+)’正则表达式的字符区段(全大写的单词)用r’ \1’替换,也就是用空格区分开每一个单词。

第三个替换,是对第二个替换后的字符串,对符合’([A-Z][a-z]+)’正则表达式的字符区段(也就是首字母大写,其他字母小写的词语)用r’ \1’替换,也是将单词用空格分隔开。

8种定时任务方案实现

目录

  • 利用while True: + sleep()实现定时任务

  • 使用Timeloop库运行定时任务

  • 利用threading.Timer实现定时任务

  • 利用内置模块sched实现定时任务

  • 利用调度模块schedule实现定时任务

  • 利用任务框架APScheduler实现定时任务

    • Job 作业
    • Trigger 触发器
    • Executor 执行器
    • Jobstore 作业存储
    • Event 事件
    • 调度器
    • APScheduler中的重要概念
    • Scheduler的工作流程
  • 使用分布式消息系统Celery实现定时任务

  • 使用数据流工具Apache Airflow实现定时任务

    • Airflow 产生的背景
    • Airflow 核心概念
    • Airflow 的架构

利用while True: + sleep()实现定时任务

位于 time 模块中的 sleep(secs) 函数,可以实现令当前执行的线程暂停 secs 秒后再继续执行。所谓暂停,即令当前线程进入阻塞状态,当达到 sleep() 函数规定的时间后,再由阻塞状态转为就绪状态,等待 CPU 调度。

基于这样的特性我们可以通过while死循环+sleep()的方式实现简单的定时任务。

代码示例:

import datetime
import time
def time_printer():
    now = datetime.datetime.now()
    ts = now.strftime('%Y-%m-%d %H:%M:%S')
    print('do func time :', ts)
def loop_monitor():
    while True:
        time_printer()
        time.sleep(5)  # 暂停5秒
if __name__ == "__main__":
    loop_monitor()

主要缺点:

  • 只能设定间隔,不能指定具体的时间,比如每天早上8:00
  • sleep 是一个阻塞函数,也就是说 sleep 这一段时间,程序什么也不能操作。

使用Timeloop库运行定时任务

Timeloop是一个库,可用于运行多周期任务。这是一个简单的库,它使用decorator模式在线程中运行标记函数。

示例代码:

import time
from timeloop import Timeloop
from datetime import timedelta
tl = Timeloop()
@tl.job(interval=timedelta(seconds=2))
def sample_job_every_2s():
    print "2s job current time : {}".format(time.ctime())
@tl.job(interval=timedelta(seconds=5))
def sample_job_every_5s():
    print "5s job current time : {}".format(time.ctime())
@tl.job(interval=timedelta(seconds=10))
def sample_job_every_10s():
    print "10s job current time : {}".format(time.ctime())

利用threading.Timer实现定时任务

threading 模块中的 Timer 是一个非阻塞函数,比 sleep 稍好一点,timer最基本理解就是定时器,我们可以启动多个定时任务,这些定时器任务是异步执行,所以不存在等待顺序执行问题。

Timer(interval, function, args=[ ], kwargs={ })

  • interval: 指定的时间
  • function: 要执行的方法
  • args/kwargs: 方法的参数

代码示例:

import threading
def func1(a):
    #Do something
    print('Do something')
    a+=1
    print(a)
    print('当前线程数为{}'.format(threading.activeCount()))
    if a>5:
        return
    t=threading.Timer(5,func1,(a,))
    t.start()

备注:Timer只能执行一次,这里需要循环调用,否则只能执行一次

利用内置模块sched实现定时任务

sched模块实现了一个通用事件调度器,在调度器类使用一个延迟函数等待特定的时间,执行任务。同时支持多线程应用程序,在每个任务执行后会立刻调用延时函数,以确保其他线程也能执行。

class sched.scheduler(timefunc, delayfunc)这个类定义了调度事件的通用接口,它需要外部传入两个参数,timefunc是一个没有参数的返回时间类型数字的函数(常用使用的如time模块里面的time),delayfunc应该是一个需要一个参数来调用、与timefunc的输出兼容、并且作用为延迟多个时间单位的函数(常用的如time模块的sleep)。

代码示例:

import datetime
import time
import sched
def time_printer():
    now = datetime.datetime.now()
    ts = now.strftime('%Y-%m-%d %H:%M:%S')
    print('do func time :', ts)
    loop_monitor()
def loop_monitor():
    s = sched.scheduler(time.time, time.sleep)  # 生成调度器
    s.enter(5, 1, time_printer, ())
    s.run()
if __name__ == "__main__":
    loop_monitor()

scheduler对象主要方法:

  • enter(delay, priority, action, argument),安排一个事件来延迟delay个时间单位。
  • cancel(event):从队列中删除事件。如果事件不是当前队列中的事件,则该方法将跑出一个ValueError。
  • run():运行所有预定的事件。这个函数将等待(使用传递给构造函数的delayfunc()函数),然后执行事件,直到不再有预定的事件。

个人点评:比threading.Timer更好,不需要循环调用。

利用调度模块schedule实现定时任务

schedule是一个第三方轻量级的任务调度模块,可以按照秒,分,小时,日期或者自定义事件执行时间。schedule允许用户使用简单、人性化的语法以预定的时间间隔定期运行Python函数(或其它可调用函数)。

先来看代码,是不是不看文档就能明白什么意思?

import schedule
import time
def job():
    print("I'm working...")
schedule.every(10).seconds.do(job)
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)
while True:
    schedule.run_pending()
    time.sleep(1)

装饰器:通过 @repeat() 装饰静态方法

import time
from schedule import every, repeat, run_pending
@repeat(every().second)
def job():
    print('working...')
while True:
    run_pending()
    time.sleep(1)

传递参数:

import schedule
def greet(name):
    print('Hello', name)
schedule.every(2).seconds.do(greet, name='Alice')
schedule.every(4).seconds.do(greet, name='Bob')
while True:
    schedule.run_pending()

装饰器同样能传递参数:

from schedule import every, repeat, run_pending
@repeat(every().second, 'World')
@repeat(every().minute, 'Mars')
def hello(planet):
    print('Hello', planet)
while True:
    run_pending()

取消任务:

import schedule
i = 0
def some_task():
    global i
    i += 1
    print(i)
    if i == 10:
        schedule.cancel_job(job)
        print('cancel job')
        exit(0)
job = schedule.every().second.do(some_task)
while True:
    schedule.run_pending()

运行一次任务:

import time
import schedule
def job_that_executes_once():
    print('Hello')
    return schedule.CancelJob
schedule.every().minute.at(':34').do(job_that_executes_once)
while True:
    schedule.run_pending()
    time.sleep(1)

根据标签检索任务:

# 检索所有任务:schedule.get_jobs()
import schedule
def greet(name):
    print('Hello {}'.format(name))
schedule.every().day.do(greet, 'Andrea').tag('daily-tasks', 'friend')
schedule.every().hour.do(greet, 'John').tag('hourly-tasks', 'friend')
schedule.every().hour.do(greet, 'Monica').tag('hourly-tasks', 'customer')
schedule.every().day.do(greet, 'Derek').tag('daily-tasks', 'guest')
friends = schedule.get_jobs('friend')
print(friends)

根据标签取消任务:

# 取消所有任务:schedule.clear()
import schedule
def greet(name):
    print('Hello {}'.format(name))
    if name == 'Cancel':
        schedule.clear('second-tasks')
        print('cancel second-tasks')
schedule.every().second.do(greet, 'Andrea').tag('second-tasks', 'friend')
schedule.every().second.do(greet, 'John').tag('second-tasks', 'friend')
schedule.every().hour.do(greet, 'Monica').tag('hourly-tasks', 'customer')
schedule.every(5).seconds.do(greet, 'Cancel').tag('daily-tasks', 'guest')
while True:
    schedule.run_pending()

运行任务到某时间:

import schedule
from datetime import datetime, timedelta, time
def job():
    print('working...')
schedule.every().second.until('23:59').do(job)  # 今天23:59停止
schedule.every().second.until('2030-01-01 18:30').do(job)  # 2030-01-01 18:30停止
schedule.every().second.until(timedelta(hours=8)).do(job)  # 8小时后停止
schedule.every().second.until(time(23, 59, 59)).do(job)  # 今天23:59:59停止
schedule.every().second.until(datetime(2030, 1, 1, 18, 30, 0)).do(job)  # 2030-01-01 18:30停止
while True:
    schedule.run_pending()

马上运行所有任务(主要用于测试):

import schedule
def job():
    print('working...')
def job1():
    print('Hello...')
schedule.every().monday.at('12:40').do(job)
schedule.every().tuesday.at('16:40').do(job1)
schedule.run_all()
schedule.run_all(delay_seconds=3)  # 任务间延迟3秒

并行运行:使用 Python 内置队列实现:

import threading
import time
import schedule
def job1():
    print("I'm running on thread %s" % threading.current_thread())
def job2():
    print("I'm running on thread %s" % threading.current_thread())
def job3():
    print("I'm running on thread %s" % threading.current_thread())
def run_threaded(job_func):
    job_thread = threading.Thread(target=job_func)
    job_thread.start()
schedule.every(10).seconds.do(run_threaded, job1)
schedule.every(10).seconds.do(run_threaded, job2)
schedule.every(10).seconds.do(run_threaded, job3)
while True:
    schedule.run_pending()
    time.sleep(1)

利用任务框架APScheduler实现定时任务

APScheduler(advanceded python scheduler)基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便。提供了基于日期、固定时间间隔以及crontab类型的任务,并且可以持久化任务。基于这些功能,我们可以很方便的实现一个Python定时任务系统。

它有以下三个特点:

  • 类似于 Liunx Cron 的调度程序(可选的开始/结束时间)
  • 基于时间间隔的执行调度(周期性调度,可选的开始/结束时间)
  • 一次性执行任务(在设定的日期/时间运行一次任务)

APScheduler有四种组成部分:

  • 触发器(trigger) 包含调度逻辑,每一个作业有它自己的触发器,用于决定接下来哪一个作业会运行。除了他们自己初始配置意外,触发器完全是无状态的。
  • 作业存储(job store) 存储被调度的作业,默认的作业存储是简单地把作业保存在内存中,其他的作业存储是将作业保存在数据库中。一个作业的数据讲在保存在持久化作业存储时被序列化,并在加载时被反序列化。调度器不能分享同一个作业存储。
  • 执行器(executor) 处理作业的运行,他们通常通过在作业中提交制定的可调用对象到一个线程或者进城池来进行。当作业完成时,执行器将会通知调度器。
  • 调度器(scheduler) 是其他的组成部分。你通常在应用只有一个调度器,应用的开发者通常不会直接处理作业存储、调度器和触发器,相反,调度器提供了处理这些的合适的接口。配置作业存储和执行器可以在调度器中完成,例如添加、修改和移除作业。通过配置executor、jobstore、trigger,使用线程池(ThreadPoolExecutor默认值20)或进程池(ProcessPoolExecutor 默认值5)并且默认最多3个(max_instances)任务实例同时运行,实现对job的增删改查等调度控制

示例代码:

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime
# 输出时间
def job():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
# BlockingScheduler
sched = BlockingScheduler()
sched.add_job(my_job, 'interval', seconds=5, id='my_job_id')
sched.start()

APScheduler中的重要概念

Job 作业

Job作为APScheduler最小执行单位。创建Job时指定执行的函数,函数中所需参数,Job执行时的一些设置信息。

构建说明:

  • id:指定作业的唯一ID
  • name:指定作业的名字
  • trigger:apscheduler定义的触发器,用于确定Job的执行时间,根据设置的trigger规则,计算得到下次执行此job的时间, 满足时将会执行
  • executor:apscheduler定义的执行器,job创建时设置执行器的名字,根据字符串你名字到scheduler获取到执行此job的 执行器,执行job指定的函数
  • max_instances:执行此job的最大实例数,executor执行job时,根据job的id来计算执行次数,根据设置的最大实例数来确定是否可执行
  • next_run_time:Job下次的执行时间,创建Job时可以指定一个时间[datetime],不指定的话则默认根据trigger获取触发时间
  • misfire_grace_time:Job的延迟执行时间,例如Job的计划执行时间是21:00:00,但因服务重启或其他原因导致21:00:31才执行,如果设置此key为40,则该job会继续执行,否则将会丢弃此job
  • coalesce:Job是否合并执行,是一个bool值。例如scheduler停止20s后重启启动,而job的触发器设置为5s执行一次,因此此job错过了4个执行时间,如果设置为是,则会合并到一次执行,否则会逐个执行
  • func:Job执行的函数
  • args:Job执行函数需要的位置参数
  • kwargs:Job执行函数需要的关键字参数

Trigger 触发器

Trigger绑定到Job,在scheduler调度筛选Job时,根据触发器的规则计算出Job的触发时间,然后与当前时间比较确定此Job是否会被执行,总之就是根据trigger规则计算出下一个执行时间。

目前APScheduler支持触发器:

  • 指定时间的DateTrigger
  • 指定间隔时间的IntervalTrigger
  • 像Linux的crontab一样的CronTrigger。

触发器参数:date

date定时,作业只执行一次。

  • run_date (datetime|str) – the date/time to run the job at
  • timezone (datetime.tzinfo|str) – time zone for run_date if it doesn’t have one already
sched.add_job(my_job, 'date', run_date=date(2009, 11, 6), args=['text'])
sched.add_job(my_job, 'date', run_date=datetime(2019, 7, 6, 16, 30, 5), args=['text'])

触发器参数:interval

interval间隔调度

  • weeks (int) – 间隔几周
  • days (int) – 间隔几天
  • hours (int) – 间隔几小时
  • minutes (int) – 间隔几分钟
  • seconds (int) – 间隔多少秒
  • start_date (datetime|str) – 开始日期
  • end_date (datetime|str) – 结束日期
  • timezone (datetime.tzinfo|str) – 时区
sched.add_job(job_function, 'interval', hours=2)

触发器参数:cron

cron调度

  • (int|str) 表示参数既可以是int类型,也可以是str类型
  • (datetime | str) 表示参数既可以是datetime类型,也可以是str类型
  • year (int|str) – 4-digit year -(表示四位数的年份,如2008年)
  • month (int|str) – month (1-12) -(表示取值范围为1-12月)
  • day (int|str) – day of the (1-31) -(表示取值范围为1-31日)
  • week (int|str) – ISO week (1-53) -(格里历2006年12月31日可以写成2006年-W52-7(扩展形式)或2006W527(紧凑形式))
  • day_of_week (int|str) – number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun) – (表示一周中的第几天,既可以用0-6表示也可以用其英语缩写表示)
  • hour (int|str) – hour (0-23) – (表示取值范围为0-23时)
  • minute (int|str) – minute (0-59) – (表示取值范围为0-59分)
  • second (int|str) – second (0-59) – (表示取值范围为0-59秒)
  • start_date (datetime|str) – earliest possible date/time to trigger on (inclusive) – (表示开始时间)
  • end_date (datetime|str) – latest possible date/time to trigger on (inclusive) – (表示结束时间)
  • timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations (defaults to scheduler timezone) -(表示时区取值)

CronTrigger可用的表达式:

表达式 参数类型 描述
* 所有 通配符。例:minutes=*即每分钟触发
* / a 所有 每隔时长a执行一次。例:minutes=”* / 3″ 即每隔3分钟执行一次
a – b 所有 a – b的范围内触发。例:minutes=“2-5”。即2到5分钟内每分钟执行一次
a – b / c 所有 a – b范围内,每隔时长c执行一次。
xth y 第几个星期几触发。x为第几个,y为星期几
last x 一个月中,最后一个星期的星期几触发
last 一个月中的最后一天触发
x, y, z 所有 组合表达式,可以组合确定值或上述表达式
# 6-8,11-12月第三个周五 00:00, 01:00, 02:00, 03:00运行
sched.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# 每周一到周五运行 直到2024-05-30 00:00:00
sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2024-05-30'

Executor 执行器

Executor在scheduler中初始化,另外也可通过scheduler的add_executor动态添加Executor。每个executor都会绑定一个alias,这个作为唯一标识绑定到Job,在实际执行时会根据Job绑定的executor找到实际的执行器对象,然后根据执行器对象执行Job。

Executor的种类会根据不同的调度来选择,如果选择AsyncIO作为调度的库,那么选择AsyncIOExecutor,如果选择tornado作为调度的库,选择TornadoExecutor,如果选择启动进程作为调度,选择ThreadPoolExecutor或者ProcessPoolExecutor都可以。

Executor的选择需要根据实际的scheduler来选择不同的执行器。目前APScheduler支持的Executor:

  • executors.asyncio:同步io,阻塞
  • executors.gevent:io多路复用,非阻塞
  • executors.pool: 线程ThreadPoolExecutor和进程ProcessPoolExecutor
  • executors.twisted:基于事件驱动

Jobstore 作业存储

Jobstore在scheduler中初始化,另外也可通过scheduler的add_jobstore动态添加Jobstore。每个jobstore都会绑定一个alias,scheduler在Add Job时,根据指定的jobstore在scheduler中找到相应的jobstore,并将job添加到jobstore中。作业存储器决定任务的保存方式, 默认存储在内存中(MemoryJobStore),重启后就没有了。APScheduler支持的任务存储器有:

  • jobstores.memory:内存
  • jobstores.mongodb:存储在mongodb
  • jobstores.redis:存储在redis
  • jobstores.rethinkdb:存储在rethinkdb
  • jobstores.sqlalchemy:支持sqlalchemy的数据库如mysql,sqlite等
  • jobstores.zookeeper:zookeeper

不同的任务存储器可以在调度器的配置中进行配置(见调度器)

Event 事件

Event是APScheduler在进行某些操作时触发相应的事件,用户可以自定义一些函数来监听这些事件,当触发某些Event时,做一些具体的操作。常见的比如。Job执行异常事件 EVENT_JOB_ERROR。Job执行时间错过事件 EVENT_JOB_MISSED。

目前APScheduler定义的Event:

  • EVENT_SCHEDULER_STARTED
  • EVENT_SCHEDULER_START
  • EVENT_SCHEDULER_SHUTDOWN
  • EVENT_SCHEDULER_PAUSED
  • EVENT_SCHEDULER_RESUMED
  • EVENT_EXECUTOR_ADDED
  • EVENT_EXECUTOR_REMOVED
  • EVENT_JOBSTORE_ADDED
  • EVENT_JOBSTORE_REMOVED
  • EVENT_ALL_JOBS_REMOVED
  • EVENT_JOB_ADDED
  • EVENT_JOB_REMOVED
  • EVENT_JOB_MODIFIED
  • EVENT_JOB_EXECUTED
  • EVENT_JOB_ERROR
  • EVENT_JOB_MISSED
  • EVENT_JOB_SUBMITTED
  • EVENT_JOB_MAX_INSTANCES

Listener表示用户自定义监听的一些Event,比如当Job触发了EVENT_JOB_MISSED事件时可以根据需求做一些其他处理。

调度器

Scheduler是APScheduler的核心,所有相关组件通过其定义。scheduler启动之后,将开始按照配置的任务进行调度。除了依据所有定义Job的trigger生成的将要调度时间唤醒调度之外。当发生Job信息变更时也会触发调度。

APScheduler支持的调度器方式如下,比较常用的为BlockingScheduler和BackgroundScheduler

  • BlockingScheduler:适用于调度程序是进程中唯一运行的进程,调用start函数会阻塞当前线程,不能立即返回。
  • BackgroundScheduler:适用于调度程序在应用程序的后台运行,调用start后主线程不会阻塞。
  • AsyncIOScheduler:适用于使用了asyncio模块的应用程序。
  • GeventScheduler:适用于使用gevent模块的应用程序。
  • TwistedScheduler:适用于构建Twisted的应用程序。
  • QtScheduler:适用于构建Qt的应用程序。

Scheduler的工作流程

Scheduler添加job流程:

Scheduler调度流程:

使用分布式消息系统Celery实现定时任务

Celery是一个简单,灵活,可靠的分布式系统,用于处理大量消息,同时为操作提供维护此类系统所需的工具, 也可用于任务调度。Celery 的配置比较麻烦,如果你只是需要一个轻量级的调度工具,Celery 不会是一个好选择。

Celery 是一个强大的分布式任务队列,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。异步任务比如是发送邮件、或者文件上传, 图像处理等等一些比较耗时的操作 ,定时任务是需要在特定时间执行的任务。

需要注意,celery本身并不具备任务的存储功能,在调度任务的时候肯定是要把任务存起来的,因此在使用celery的时候还需要搭配一些具备存储、访问功能的工具,比如:消息队列、Redis缓存、数据库等。官方推荐的是消息队列RabbitMQ,有些时候使用Redis也是不错的选择。

它的架构组成如下图:

Celery架构,它采用典型的生产者-消费者模式,主要由以下部分组成:

  • Celery Beat,任务调度器,Beat进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给任务队列。
  • Producer:需要在队列中进行的任务,一般由用户、触发器或其他操作将任务入队,然后交由workers进行处理。调用了Celery提供的API、函数或者装饰器而产生任务并交给任务队列处理的都是任务生产者。
  • Broker,即消息中间件,在这指任务队列本身,Celery扮演生产者和消费者的角色,brokers就是生产者和消费者存放/获取产品的地方(队列)。
  • Celery Worker,执行任务的消费者,从队列中取出任务并执行。通常会在多台服务器运行多个消费者来提高执行效率。
  • Result Backend:任务处理完后保存状态信息和结果,以供查询。Celery默认已支持Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy等方式。

实际应用中,用户从Web前端发起一个请求,我们只需要将请求所要处理的任务丢入任务队列broker中,由空闲的worker去处理任务即可,处理的结果会暂存在后台数据库backend中。我们可以在一台机器或多台机器上同时起多个worker进程来实现分布式地并行处理任务。

Celery定时任务实例:

  • Python Celery & RabbitMQ Tutorial
  • Celery 配置实践笔记

使用数据流工具Apache Airflow实现定时任务

Apache Airflow 是Airbnb开源的一款数据流程工具,目前是Apache孵化项目。以非常灵活的方式来支持数据的ETL过程,同时还支持非常多的插件来完成诸如HDFS监控、邮件通知等功能。Airflow支持单机和分布式两种模式,支持Master-Slave模式,支持Mesos等资源调度,有非常好的扩展性。被大量公司采用。

Airflow使用Python开发,它通过DAGs(Directed Acyclic Graph, 有向无环图)来表达一个工作流中所要执行的任务,以及任务之间的关系和依赖。比如,如下的工作流中,任务T1执行完成,T2和T3才能开始执行,T2和T3都执行完成,T4才能开始执行。

Airflow提供了各种Operator实现,可以完成各种任务实现:

  • BashOperator – 执行 bash 命令或脚本。
  • SSHOperator – 执行远程 bash 命令或脚本(原理同 paramiko 模块)。
  • PythonOperator – 执行 Python 函数。
  • EmailOperator – 发送 Email。
  • HTTPOperator – 发送一个 HTTP 请求。
  • MySqlOperator, SqliteOperator, PostgresOperator, MsSqlOperator, OracleOperator, JdbcOperator, 等,执行 SQL 任务。
  • DockerOperator, HiveOperator, S3FileTransferOperator, PrestoToMysqlOperator, SlackOperator…

除了以上这些 Operators 还可以方便的自定义 Operators 满足个性化的任务需求。

一些情况下,我们需要根据执行结果执行不同的任务,这样工作流会产生分支。如:

这种需求可以使用BranchPythonOperator来实现。

Airflow 产生的背景

通常,在一个运维系统,数据分析系统,或测试系统等大型系统中,我们会有各种各样的依赖需求。包括但不限于:

  • 时间依赖:任务需要等待某一个时间点触发。
  • 外部系统依赖:任务依赖外部系统需要调用接口去访问。
  • 任务间依赖:任务 A 需要在任务 B 完成后启动,两个任务互相间会产生影响。
  • 资源环境依赖:任务消耗资源非常多, 或者只能在特定的机器上执行。

crontab 可以很好地处理定时执行任务的需求,但仅能管理时间上的依赖。Airflow 的核心概念 DAG(有向无环图)—— 来表现工作流。

  • Airflow 是一种 WMS,即:它将任务以及它们的依赖看作代码,按照那些计划规范任务执行,并在实际工作进程之间分发需执行的任务。
  • Airflow 提供了一个用于显示当前活动任务和过去任务状态的优秀 UI,并允许用户手动管理任务的执行和状态。
  • Airflow 中的工作流是具有方向性依赖的任务集合。
  • DAG 中的每个节点都是一个任务,DAG 中的边表示的是任务之间的依赖(强制为有向无环,因此不会出现循环依赖,从而导致无限执行循环)。

Airflow 核心概念

  • DAGs:即有向无环图(Directed Acyclic Graph),将所有需要运行的tasks按照依赖关系组织起来,描述的是所有tasks执行顺序。
  • Operators:可以简单理解为一个class,描述了DAG中某个的task具体要做的事。其中,airflow内置了很多operators,如BashOperator 执行一个bash 命令,PythonOperator 调用任意的Python 函数,EmailOperator 用于发送邮件,HTTPOperator 用于发送HTTP请求, SqlOperator 用于执行SQL命令等等,同时,用户可以自定义Operator,这给用户提供了极大的便利性。
  • Tasks:Task 是 Operator的一个实例,也就是DAGs中的一个node。
  • Task Instance:task的一次运行。Web 界面中可以看到task instance 有自己的状态,包括”running”, “success”, “failed”, “skipped”, “up for retry”等。
  • Task Relationships:DAGs中的不同Tasks之间可以有依赖关系,如 Task1 >> Task2,表明Task2依赖于Task2了。通过将DAGs和Operators结合起来,用户就可以创建各种复杂的 工作流(workflow)。

Airflow 的架构

在一个可扩展的生产环境中,Airflow 含有以下组件:

  • 元数据库:这个数据库存储有关任务状态的信息。
  • 调度器:Scheduler 是一种使用 DAG 定义结合元数据中的任务状态来决定哪些任务需要被执行以及任务执行优先级的过程。调度器通常作为服务运行。
  • 执行器:Executor 是一个消息队列进程,它被绑定到调度器中,用于确定实际执行每个任务计划的工作进程。有不同类型的执行器,每个执行器都使用一个指定工作进程的类来执行任务。例如,LocalExecutor 使用与调度器进程在同一台机器上运行的并行进程执行任务。其他像 CeleryExecutor 的执行器使用存在于独立的工作机器集群中的工作进程执行任务。
  • Workers:这些是实际执行任务逻辑的进程,由正在使用的执行器确定。

Worker的具体实现由配置文件中的executor来指定,airflow支持多种Executor:

  • SequentialExecutor: 单进程顺序执行,一般只用来测试
  • LocalExecutor: 本地多进程执行
  • CeleryExecutor: 使用Celery进行分布式任务调度
  • DaskExecutor:使用Dask进行分布式任务调度
  • KubernetesExecutor: 1.10.0新增, 创建临时POD执行每次任务

生产环境一般使用CeleryExecutor和KubernetesExecutor。

使用CeleryExecutor的架构如图:

使用KubernetesExecutor的架构如图:

100个Python小技巧

▍1、for循环中的else条件

这是一个for-else方法,循环遍历列表时使用else语句。

下面举个例子,比如我们想检查一个列表中是否包含奇数。

那么可以通过for循环,遍历查找。

numbers = [2, 4, 6, 8, 1]

for number in numbers:
    if number % 2 == 1:
        print(number)
        break
else:
    print("No odd numbers")

如果找到了奇数,就会打印该数值,并且执行break语句,跳过else语句。

没有的话,就不会执行break语句,而是执行else语句。

2、从列表中获取元素,定义多个变量

my_list = [1, 2, 3, 4, 5]
one, two, three, four, five = my_list

▍3、使用heapq模块,获取列表中n个最大或最小的元素

import heapq

scores = [51, 33, 64, 87, 91, 75, 15, 49, 33, 82]

print(heapq.nlargest(3, scores))  # [91, 87, 82]
print(heapq.nsmallest(5, scores))  # [15, 33, 33, 49, 51]

▍4、将列表中的所有元素作为参数传递给函数

我们可以使用 * 号,提取列表中所有的元素

my_list = [1, 2, 3, 4]

print(my_list)  # [1, 2, 3, 4]
print(*my_list)  # 1 2 3 4

如此便可以将列表中的所有元素,作为参数传递给函数

def sum_of_elements(*arg):
    total = 0
    for i in arg:
        total += i

    return total


result = sum_of_elements(*[1, 2, 3, 4])
print(result)  # 10

▍5、获取列表的所有中间元素

_, *elements_in_the_middle, _ = [1, 2, 3, 4, 5, 6, 7, 8]
print(elements_in_the_middle)  # [2, 3, 4, 5, 6, 7]

▍6、使用一行代码赋值多个变量

one, two, three, four = 1, 2, 3, 4

▍7、列表推导式

只用一行代码,便可完成对数组的迭代以及运算。

比如,将列表中的每个数字提高一倍。

numbers = [1, 2, 3, 4, 5]
squared_numbers = [num * num for num in numbers]

print(squared_numbers) # [1, 4, 9, 16, 25]

推导式不仅列表能用,字典、集合、生成器也能使用。

下面看一下,使用字典推导式,将字典的值提高一倍。

dictionary = {'a': 4, 'b': 5}
squared_dictionary = {key: num * num for (key, num) in dictionary.items()}

print(squared_dictionary)  # {'a': 16, 'b': 25}

▍8、通过Enum枚举同一标签或一系列常量的集合

枚举是绑定到唯一的常量值的一组符号名称(成员)。

在枚举中,成员可以通过身份进行比较,枚举本身可以迭代。

from enum import Enum


class Status(Enum):
    NO_STATUS = -1
    NOT_STARTED = 0
    IN_PROGRESS = 1
    COMPLETED = 2


print(Status.IN_PROGRESS.name)  # IN_PROGRESS
print(Status.COMPLETED.value)  # 2

▍9、重复字符串

name = "Banana"
print(name * 4)  # BananaBananaBananaBanana

▍10、比较3个数字的大小

如果想比较一个值和其他两个值的大小情况,你可以使用简单的数学表达式。

1 < x < 10

这个是最简单的代数表达式,在Python中也是可以使用的。

x = 3

print(1 < x < 10)  # True
print(1 < x and x < 10)  # True

▍11、使用1行代码合并字典

first_dictionary = {'name': 'Fan', 'location': 'Guangzhou'}
second_dictionary = {'name': 'Fan', 'surname': 'Xiao', 'location': 'Guangdong, Guangzhou'}

result = first_dictionary | second_dictionary

print(result)
# {'name': 'Fan', 'location': 'Guangdong, Guangzhou', 'surname': 'Xiao'}

▍12、查找元组中元素的索引

books = ('Atomic habits', 'Ego is the enemy', 'Outliers', 'Mastery')

print(books.index('Mastery'))   # 3

▍13、将字符串转换为字符串列表

假设你在函数中获得输出,原本应该是一个列表,但实际上却是一个字符串。

input = "[1,2,3]"

你可能第一时间会想到使用索引或者正则表达式。

实际上,使用ast模块的literal_eval方法就能搞定。

import ast


def string_to_list(string):
    return ast.literal_eval(string)


string = "[1, 2, 3]"
my_list = string_to_list(string)
print(my_list)  # [1, 2, 3]

string = "[[1, 2, 3],[4, 5, 6]]"
my_list = string_to_list(string)
print(my_list)  # [[1, 2, 3], [4, 5, 6]]

▍14、计算两数差值

计算出2个数字之间的差值。

def subtract(a, b):
    return a - b


print((subtract(1, 3)))  # -2
print((subtract(3, 1)))  # 2

上面的这个方法,需要考虑数值的先后顺序。

def subtract(a, b):
    return a - b


print((subtract(a=1, b=3)))  # -2
print((subtract(b=3, a=1)))  # -2

使用命名参数,安排顺序,这样就不会出错了。

▍15、用一个print()语句打印多个元素

print(1, 2, 3, "a", "z", "this is here", "here is something else")

▍16、在同一行打印多个元素

print("Hello", end="")
print("World")  # HelloWorld

print("Hello", end=" ")
print("World")  # Hello World

print('words',   'with', 'commas', 'in', 'between', sep=', ')
# words, with, commas, in, between

▍17、打印多个值,在每个值之间使用自定义分隔符

print("29", "01", "2022", sep="/")  # 29/01/2022

print("name", "domain.com", sep="@")  # name@domain.com

▍18、不能在变量名的开头使用数字

four_letters = "abcd" # this works

4_letters = "abcd" # this doesn’t work

这是Python的变量命名规则。

▍19、不能在变量名的开头使用运算符

+variable = "abcd"  # this doesn’t work

▍20、数字的第一位不能是0

number = 0110 # this doesn't work

这个确实挺神奇的。

▍21、在变量名的任何地方使用下划线

a______b = "abcd"  # this works
_a_b_c_d = "abcd"  # this also works

这并不意味着,你可以无限使用,为了代码的易读性,还是需要合理使用。

▍22、使用下划线分割数值较大的数字

print(1_000_000_000)  # 1000000000
print(1_234_567)  # 1234567

如此,看到一大堆数字时,也能轻松阅读。

▍23、反转列表

my_list = ['a', 'b', 'c', 'd']

my_list.reverse()

print(my_list)  # ['d', 'c', 'b', 'a']

▍24、使用步进函数对字符串切片

my_string = "This is just a sentence"
print(my_string[0:5])  # This

# Take three steps forward
print(my_string[0:10:3])  # Tsse

▍25、反向切片

my_string = "This is just a sentence"
print(my_string[10:0:-1])  # suj si sih

# Take two steps forward
print(my_string[10:0:-2])  # sjs i

▍26、使用开始或结束索引进行切片

my_string = "This is just a sentence"
print(my_string[4:])  # is just a sentence

print(my_string[:3])  # Thi

▍27、/和//的区别

print(3/2)  # 1.5
print(3//2)  # 1

▍28、==和is的区别

is:检查两个变量是否指向同一对象内存中

==:比较两个对象的值

first_list = [1, 2, 3]
second_list = [1, 2, 3]

# 比较两个值
print(first_list == second_list)  # True

# 是否指向同一内存
print(first_list is second_list)  
# False


third_list = first_list

print(third_list is first_list)  
# True

▍29、合并字典

dictionary_one = {"a": 1, "b": 2}
dictionary_two = {"c": 3, "d": 4}

merged = {**dictionary_one, **dictionary_two}

print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

▍30、检查字符串是否大于另一字符串

first = "abc"
second = "def"

print(first < second)  # True

second = "ab"
print(first < second)  # False

▍31、检查字符串是否以特定字符开头(不使用索引)

my_string = "abcdef"
print(my_string.startswith("b"))  # False

▍32、使用id()查找变量的唯一id

print(id(1))  # 4325776624
print(id(2))  # 4325776656
print(id("string"))  # 4327978288

▍33、整数、浮点数、字符串、布尔值和元组都是不可变的

当变量被赋值为整数、浮点数、字符串、布尔值、元组这些不可变类型后,该变量就会指向一个内存对象。

如果重新给变量再赋值,它的内存对象就会发生改变。

number = 1
print(id(number))  # 4325215472
print(id(1))  # 4325215472

number = 3
print(id(number))  # 4325215536
print(id(1))  # 4325215472

▍34、字符串和元组也是不可变的

此处再说明一次。

name = "Fatos"
print(id(name))  # 4422282544

name = "fatos"
print(id(name))  # 4422346608

▍35、列表、集合和字典都是可变的

这意味着发生更改时,不会改变其内存对象。

cities = ["Beijing", "Guangzhou", "chengdu"]
print(id(cities))  # 4482699712

cities.append("Beijing")
print(id(cities))  # 4482699712

下面是字典。

my_set = {1, 2, 3, 4}
print(id(my_set))  # 4352726176

my_set.add(5)
print(id(my_set))  # 4352726176

▍36、把一个列表变成不可变的列表

my_set = frozenset(['a', 'b', 'c', 'd'])

my_set.add("a")

使用frozenset()后,你就无法更改了。

▍37、if-elif块可以在没有else块的情况下存在

但是elif不能在没有if语句之前独立存在。

def check_number(number):
    if number > 0:
        return "Positive"
    elif number == 0:
        return "Zero"

    return "Negative"


print(check_number(1))  # Positive

▍38、使用sorted()检查2个字符串是否为相同

def check_if_anagram(first_word, second_word):
    first_word = first_word.lower()
    second_word = second_word.lower()
    return sorted(first_word) == sorted(second_word)


print(check_if_anagram("testinG", "Testing"))  # True
print(check_if_anagram("Here", "Rehe"))  # True
print(check_if_anagram("Know", "Now"))  # False

▍39、获取字符的Unicode值

print(ord("A"))  # 65
print(ord("B"))  # 66
print(ord("C"))  # 66
print(ord("a"))  # 97

▍40、获取字典的键

dictionary = {"a": 1, "b": 2, "c": 3}

keys = dictionary.keys()
print(list(keys))  # ['a', 'b', 'c']

▍41、获取字典的值

dictionary = {"a": 1, "b": 2, "c": 3}

values = dictionary.values()
print(list(values))  # [1, 2, 3]

▍42、交换字典的键、值位置

dictionary = {"a": 1, "b": 2, "c": 3}

reversed_dictionary = {j: i for i, j in dictionary.items()}
print(reversed)  # {1: 'a', 2: 'b', 3: 'c'}

▍43、将布尔值转换为数字

print(int(False))  # 0
print(float(True))  # 1.0

▍44、在算术运算中使用布尔值

x = 10
y = 12

result = (x - False)/(y * True)
print(result)  # 0.8333333333333334

▍45、将任何数据类型转换为布尔值

print(bool(.0))  # False
print(bool(3))  # True
print(bool("-"))  # True
print(bool("string"))  # True
print(bool(" "))  # True

▍46、将值转换为复数

print(complex(10, 2))  # (10+2j)

也可以将数字转换为十六进制数。

print(hex(11))  # 0xb

▍47、在列表的第一个位置添加一个值

如果使用append(),将从列表的最后一个位置插入新值。

可以通过使用insert(),来指定插入新元素的索引和数值。

那么列表的第一个位置为0,即下标为0。

my_list = [3, 4, 5]

my_list.append(6)
my_list.insert(0, 2)
print(my_list)  # [2, 3, 4, 5, 6]

▍48、Lambda函数只能在一行代码中

无法通过多行代码,来使用lambda函数。

comparison = lambda x: if x > 3:
                    print("x > 3")
                else:
                    print("x is not greater than 3")

报错。

49、Lambda中的条件语句应始终包含else语句

comparison = lambda x: "x > 3" if x > 3

运行上面的代码,报错。

这是由于条件表达式的特性,而不是lambda的导致的。

▍50、使用filter(),获得一个新对象

my_list = [1, 2, 3, 4]

odd = filter(lambda x: x % 2 == 1, my_list)

print(list(odd))   # [1, 3]
print(my_list)  # [1, 2, 3, 4]

▍51、map()返回一个新对象

map()函数将给定函数应用于可迭代对象(列表、元组等),然后返回结果(map对象)。

my_list = [1, 2, 3, 4]

squared = map(lambda x: x ** 2, my_list)

print(list(squared))   # [1, 4, 9, 16]
print(my_list)  # [1, 2, 3, 4]

▍52、range()的step参数

for number in range(1, 10, 3):
    print(number, end=" ")
# 1 4 7

▍53、range()默认从0开始

def range_with_zero(number):
    for i in range(0, number):
        print(i, end=' ')


def range_with_no_zero(number):
    for i in range(number):
        print(i, end=' ')


range_with_zero(3)  # 0 1 2
range_with_no_zero(3)  # 0 1 2

▍54、不需要和0比较长度

如果长度大于0,则默认为True。

def get_element_with_comparison(my_list):
    if len(my_list) > 0:
        return my_list[0]


def get_first_element(my_list):
    if len(my_list):
        return my_list[0]


elements = [1, 2, 3, 4]
first_result = get_element_with_comparison(elements)
second_result = get_element_with_comparison(elements)

print(first_result == second_result)  # True

▍55、可以在同一个作用域内多次定义一个方法

但是,只有最后一个会被调用,覆盖以前。

def get_address():
    return "First address"


def get_address():
    return "Second address"


def get_address():
    return "Third address"


print(get_address())  # Third address

▍56、在外部直接访问私有属性

在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法。

如果想要在外部访问,那么只需要在名称前面加上 ‘_类名’ 变成 ‘_类名__名称’。

class Engineer:
    def __init__(self, name):
        self.name = name
        self.__starting_salary = 62000


dain = Engineer('Dain')
print(dain._Engineer__starting_salary)  # 62000

▍57、检查对象的内存使用情况

import sys

print(sys.getsizeof("bitcoin"))  # 56

▍58、定义一个方法,可以调用任意个参数

def get_sum(*arguments):
    result = 0
    for i in arguments:
        result += i
    return result


print(get_sum(1, 2, 3))  # 6
print(get_sum(1, 2, 3, 4, 5))  # 15
print(get_sum(1, 2, 3, 4, 5, 6, 7))  # 28

▍59、使用super()或父类的名称调用父类的初始化

使用super函数调用父类的初始化方法。

class Parent:
    def __init__(self, city, address):
        self.city = city
        self.address = address


class Child(Parent):
    def __init__(self, city, address, university):
        super().__init__(city, address)
        self.university = university


child = Child('Peking University', 'Fudan University', 'Tsinghua University')
print(child.university)  # Tsinghua University

使用父类的名称调用父类。

class Parent:
    def __init__(self, city, address):
        self.city = city
        self.address = address


class Child(Parent):
    def __init__(self, city, address, university):
        Parent.__init__(self, city, address)
        self.university = university


child = Child('Peking University', 'Fudan University', 'Tsinghua University')
print(child.university)  # Tsinghua University

▍60、在类中使用 + 操作符

在两个int数据类型之间使用 + 运算符时,将得到它们的和。

而在两个字符串数据类型之间使用它时,会将其合并。

print(10 + 1)  # 两数相加
print('first' + 'second')  # 字符串相加

这个就是操作符重载,你还可以在类中使用(add)。

class Expenses:
    def __init__(self, rent, groceries):
        self.rent = rent
        self.groceries = groceries

    def __add__(self, other):
        return Expenses(self.rent + other.rent,
                        self.groceries + other.groceries)


april_expenses = Expenses(1000, 200)
may_expenses = Expenses(1000, 300)

total_expenses = april_expenses + may_expenses
print(total_expenses.rent)  # 2000
print(total_expenses.groceries)  # 500

▍61、在类中使用 < 和 == 操作符

下面定义一个操作重载示例( < 操作符),使用lt方法。

class Game:
    def __init__(self, score):
        self.score = score

    def __lt__(self, other):
        return self.score < other.score


first = Game(1)
second = Game(2)

print(first < second)  # True

同样的,== 操作符使用eq方法。

class Journey:
    def __init__(self, location, destination, duration):
        self.location = location
        self.destination = destination
        self.duration = duration

    def __eq__(self, other):
        return ((self.location == other.location) and
                (self.destination == other.destination) and
                (self.duration == other.duration))


first = Journey('Location A', 'Destination A', '30min')
second = Journey('Location B', 'Destination B', '30min')

print(first == second)

还有一些其他的定义。

__sub__() for -
__mul__() for *
__truediv__() for /
__ne__() for !=
__ge__() for >=
__gt__() for >

▍62、为类的对象定义自定义的可打印版本

class Rectangle:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return repr('Rectangle with area=' + str(self.a * self.b))


print(Rectangle(3, 4))  # 'Rectangle with area=12'

▍63、交换字符串中字符的大小写

string = "This is just a sentence."
result = string.swapcase()

print(result)  # tHIS IS JUST A SENTENCE.

▍64、检查字符串是否都是空格

string = "  "
result = string.isspace()

print(result)  # True

▍65、检查字符串是否都是字母或数字

name = "Password"
print(name.isalnum())  # True

name = "Secure Password "
print(name.isalnum())  # False

name = "S3cur3P4ssw0rd"
print(name.isalnum())  # True

name = "133"
print(name.isalnum())  # True

▍66、检查字符串是否都是字母

string = "Name"
print(string.isalpha())  # True

string = "Firstname Lastname"
print(string.isalpha())  # False

string = "P4ssw0rd"
print(string.isalpha())  # False

▍67、根据参数删除字符

从右侧开始。

string = "This is a sentence with       "
print(string.rstrip())  # "This is a sentence with"

string = "this here is a sentence…..,,,,aaaaasd"
print(string.rstrip(".,dsa"))  # "this here is a sentence"

同样的,左侧也能操作。

string = "ffffffffFirst"
print(string.lstrip("f"))  # First

▍68、检查字符串是否为数字

string = "seven"
print(string.isdigit())  # False

string = "1337"
print(string.isdigit())  # True

string = "5a"
print(string.isdigit())  # False

string = "2**5"
print(string.isdigit())  # False

▍69、检查字符串是否为中文数字

# 42673
string = "四二六七三"

print(string.isdigit())  # False
print(string.isnumeric())  # True

▍70、检查字符串是否所有单词都是大写开头

string = "This is a sentence"
print(string.istitle())  # False

string = "10 Python Tips"
print(string.istitle())  # True

string = "How to Print A String in Python"
# False
print(string.istitle())

string = "PYTHON"
print(string.istitle())  # False

▍71、在元组中使用负索引

numbers = (1, 2, 3, 4)

print(numbers[-1])  # 4
print(numbers[-4])  # 1

▍72、在元组中嵌套列表和元组

mixed_tuple = (("a"*10, 3, 4), ['first', 'second', 'third'])

print(mixed_tuple[1])  # ['first', 'second', 'third']
print(mixed_tuple[0])  # ('aaaaaaaaaa', 3, 4)

▍73、快速统计元素在列表中出现的次数

names = ["Besim", "Albert", "Besim", "Fisnik", "Meriton"]

print(names.count("Besim"))  # 2

▍74、使用slice()获取元素

使用slice()获取最后n个元素。

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
slicing = slice(-4, None)
print(my_list[slicing])  # [4, 5, 6]

print(my_list[-3])  # 4

使用slice()做切片任务。

string = "Data Science"

slice_object = slice(5, None)
print(string[slice_object])   # Science

▍75、计算元素在元组中出现的次数

my_tuple = ('a', 1, 'f', 'a', 5, 'a')

print(my_tuple.count('a'))  # 3

▍76、获取元组中元素的索引

my_tuple = ('a', 1, 'f', 'a', 5, 'a')

print(my_tuple.index('f'))  #  2

▍77、步进获得元组

my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

print(my_tuple[::3])  # (1, 4, 7, 10)

▍78、通过索引获取子元组

my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

print(my_tuple[3:])  # (4, 5, 6, 7, 8, 9, 10)

▍79、将列表、集合、字典中所有元素删除

my_list = [1, 2, 3, 4]
my_list.clear()
print(my_list)  # []

my_set = {1, 2, 3}
my_set.clear()
print(my_set)  # set()

my_dict = {"a": 1, "b": 2}
my_dict.clear()
print(my_dict)  # {}

▍80、合并集合

使用union()方法,返回一个新集合。

first_set = {4, 5, 6}
second_set = {1, 2, 3}

print(first_set.union(second_set))  # {1, 2, 3, 4, 5, 6}

还可以使用update()方法,将第二个集合的元素插入到第一个集合中去。

first_set = {4, 5, 6}
second_set = {1, 2, 3}

first_set.update(second_set)

print(first_set)  # {1, 2, 3, 4, 5, 6}

▍81、在函数里输出结果

def is_positive(number):
    print("Positive" if number > 0 else "Negative")  # Positive

is_positive(-3)

▍82、if语句中的多个条件

math_points = 51
biology_points = 78
physics_points = 56
history_points = 72

my_conditions = [math_points > 50, biology_points > 50,
                 physics_points > 50, history_points > 50]

if all(my_conditions):
    print("Congratulations! You have passed all of the exams.")
else:
    print("I am sorry, but it seems that you have to repeat at least one exam.")
# Congratulations! You have passed all of the exams.

▍83、在一个if语句中,至少满足多个条件中的一个

math_points = 40
biology_points = 78
physics_points = 56
history_points = 72

my_conditions = [math_points > 50, biology_points > 50,
                 physics_points > 50, history_points > 50]

if any(my_conditions):
    print("Congratulations! You have passed all of the exams.")
else:
    print("I am sorry, but it seems that you have to repeat at least one exam.")
# Congratulations! You have passed all of the exams.

▍84、任何非空字符串都为True

print(bool("Non empty"))  # True
print(bool(""))  # False

▍85、任何非空列表、元组、字典都为True

print(bool([]))  # False
print(bool(set([])))  # False

print(bool({}))  # False
print(bool({"a": 1}))  # True

▍86、None、False、0都为False

print(bool(False))  # False
print(bool(None))  # False
print(bool(0))  # False

▍87、在函数中使用全局变量

在函数无法直接修改全局变量的值。

string = "string"


def do_nothing():
  string = "inside a method"


do_nothing()

print(string)  # string

可通过修饰符global,修改全局变量的值。

string = "string"


def do_nothing():
    global string
    string = "inside a method"


do_nothing()

print(string)  # inside a method

▍88、计算字符串或列表中元素的数量

使用collections中的Counter计算字符串或列表中元素的数量。

from collections import Counter

result = Counter("Banana")
print(result)  # Counter({'a': 3, 'n': 2, 'B': 1})


result = Counter([1, 2, 1, 3, 1, 4, 1, 5, 1, 6])
print(result)  # Counter({1: 5, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1})

▍89、检查2个字符串是否为相同

可以使用Counter()方法。

from collections import Counter


def check_if_anagram(first_string, second_string):
    first_string = first_string.lower()
    second_string = second_string.lower()
    return Counter(first_string) == Counter(second_string)


print(check_if_anagram('testinG', 'Testing'))  # True
print(check_if_anagram('Here', 'Rehe'))  # True
print(check_if_anagram('Know', 'Now'))  # False

可以使用sorted()方法。

def check_if_anagram(first_word, second_word):
    first_word = first_word.lower()
    second_word = second_word.lower()
    return sorted(first_word) == sorted(second_word)


print(check_if_anagram("testinG", "Testing"))  # True
print(check_if_anagram("Here", "Rehe"))  # True
print(check_if_anagram("Know", "Now"))  # False

▍90、使用itertools中的count计算元素的数量

from itertools import count

my_vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']

current_counter = count()

string = "This is just a sentence."

for i in string:
    if i in my_vowels:
        print(f"Current vowel: {i}")
        print(f"Number of vowels found so far: {next(current_counter)}")

输出如下。

Current vowel: i
Number of vowels found so far: 0
Current vowel: i
Number of vowels found so far: 1
Current vowel: u
Number of vowels found so far: 2
Current vowel: a
Number of vowels found so far: 3
Current vowel: e
Number of vowels found so far: 4
Current vowel: e
Number of vowels found so far: 5
Current vowel: e
Number of vowels found so far: 6

▍91、对字符串或列表的元素进行次数排序

collections模块的Counter(),默认情况下是不会根据元素的频率对它们进行排序的。

from collections import Counter

result = Counter([1, 2, 3, 2, 2, 2, 2])
print(result)  # Counter({2: 5, 1: 1, 3: 1})
print(result.most_common())  # [(2, 5), (1, 1), (3, 1)]

map()函数将给定函数应用于可迭代对象(列表、元组等),然后返回结果(map对象)。

▍92、查找列表中出现频率最高的元素

my_list = ['1', 1, 0, 'a', 'b', 2, 'a', 'c', 'a']

print(max(set(my_list), key=my_list.count))  # a

▍93、copy()和deepcopy()的区别

浅拷贝: 拷贝父对象,但是不会拷贝对象的内部的子对象。

深拷贝: 拷贝父对象. 以及其内部的子对象。

下面是一个copy()的例子。

first_list = [[1, 2, 3], ['a', 'b', 'c']]

second_list = first_list.copy()

first_list[0][2] = 831

print(first_list)  # [[1, 2, 831], ['a', 'b', 'c']]
print(second_list)  # [[1, 2, 831], ['a', 'b', 'c']]

这里是一个deepcopy()的例子。

import copy

first_list = [[1, 2, 3], ['a', 'b', 'c']]

second_list = copy.deepcopy(first_list)

first_list[0][2] = 831

print(first_list)  # [[1, 2, 831], ['a', 'b', 'c']]
print(second_list)  # [[1, 2, 3], ['a', 'b', 'c']]

▍94、访问字典中不存在的键时,避免报错

如果你想访问字典一个不存在的键,代码会报错。

my_dictonary = {"name": "Name", "surname": "Surname"}
print(my_dictonary["age"])  

错误如下。

KeyError: 'age'

可以通过使用defaultdict(),代码将不会报错。

from collections import defaultdict

my_dictonary = defaultdict(str)
my_dictonary['name'] = "Name"
my_dictonary['surname'] = "Surname"

print(my_dictonary["age"])  

▍95、构建迭代器

class OddNumbers:
    def __iter__(self):
        self.a = 1
        return self

    def __next__(self):
        x = self.a
        self.a += 2
        return x


odd_numbers_object = OddNumbers()
iterator = iter(odd_numbers_object)

print(next(iterator))  # 1
print(next(iterator))  # 3
print(next(iterator))  # 5

▍96、删除列表的重复项

my_set = set([1, 2, 1, 2, 3, 4, 5])
print(list(my_set))  # [1, 2, 3, 4, 5]

▍97、打印模块的安装位置

import pandas

print(pandas)  # <module 'torch' from '/Users/...'

▍98、使用not in检查一个值是否在列表中

odd_numbers = [1, 3, 5, 7, 9]
even_numbers = []

for i in range(9):
    if i not in odd_numbers:
        even_numbers.append(i)

print(even_numbers)  # [0, 2, 4, 6, 8]

▍99、sort()和sorted()的区别

sort():对原始列表进行排序

sorted():返回一个新的排序列表

groceries = ['milk', 'bread', 'tea']

new_groceries = sorted(groceries)
# new_groceries = ['bread', 'milk', 'tea']

print(new_groceries)

# groceries = ['milk', 'bread', 'tea']
print(groceries)

groceries.sort()

# groceries = ['bread', 'milk', 'tea']
print(groceries)

▍100、使用uuid模块生成唯一ID

UUID代表唯一标识符。

import uuid

# 根据主机ID、序列号和当前时间生成UUID
print(uuid.uuid1())  # 308490b6-afe4-11eb-95f7-0c4de9a0c5af

# 生成一个随机UUID
print(uuid.uuid4())  # 93bc700b-253e-4081-a358-24b60591076a

13个有关基础数据结构的使用技巧

列表

与列表相关的6个操作,介绍如下;

1. 将两个列表合并到一个字典中

假设我们在Python中有两个列表,我们希望将它们合并为字典形式,其中一个列表的项目作为字典的键,另一个作为值。这是在用 Python 编写代码时经常遇到的一个非常常见的问题。

但是为了解决这个问题,我们需要考虑几个限制,比如两个列表的大小,两个列表中项目的类型,以及其中是否有重复的项目,尤其是我们将使用的项目 作为钥匙。我们可以通过使用像 zip 这样的内置函数来克服这个问题。

keys_list = ['A', 'B', 'C']
values_list = ['blue', 'red', 'bold']

# 有 3 种方法可以将这两个列表转换为字典
# 1.使用Python zip、dict函数
dict_method_1 = dict(zip(keys_list, values_list))

# 2. 使用带有字典推导式的 zip 函数
dict_method_2 = {key:value for key, value in zip(keys_list, values_list)}

# 3.循环使用zip函数
items_tuples = zip(keys_list, values_list) 
dict_method_3 = {} 
for key, value in items_tuples: 
    if key in dict_method_3: 
        pass 
    else: 
        dict_method_3[key] = value

print(dict_method_1)
print(dict_method_2)
print(dict_method_3)

结果如下:

2.将两个或多个列表合并为一个列表

当我们有两个或更多列表时,我们希望将它们全部收集到一个大列表中,其中较小列表的所有第一项构成较大列表中的第一个列表。

例如,如果我有 4 个列表 [1,2,3]['a','b','c']['h','e','y'], 和[4,5,6],我们想为这四个列表创建一个新列表;它将是 [[1,'a','h',4], [2,'b','e',5], [3,'c','y',6]]

def merge(*args, missing_val = None):
    max_length = max([len(lst) for lst in args])
    outList = []
    for i in range(max_length):
        outList.append([args[k][i] if i < len(args[k]) else missing_val for k in range(len(args))])
    return outList

merge([1,2,3],['a','b','c'],['h','e','y'],[4,5,6])

结果如下:

3. 对字典列表进行排序

下一组日常列表任务是排序任务。根据列表中包含的项目的数据类型,我们将采用稍微不同的方式对它们进行排序。让我们首先从对字典列表进行排序开始。

dicts_lists = [
  {
    "Name": "James",
    "Age": 20,
  },
  {
     "Name": "May",
     "Age": 14,
  },
  {
    "Name": "Katy",
    "Age": 23,
  }
]

# 方法一
dicts_lists.sort(key=lambda item: item.get("Age"))

# 方法二
from operator import itemgetter
f = itemgetter('Name')
dicts_lists.sort(key=f)

结果如下:

4. 对字符串列表进行排序

我们经常面临包含字符串的列表,我们需要按字母顺序、长度或我们想要或我们的应用程序需要的任何其他因素对这些列表进行排序。

现在,我应该提到这些是对字符串列表进行排序的直接方法,但有时您可能需要实现排序算法来解决该问题。

my_list = ["blue", "red", "green"]

# 方法一
my_list.sort() 
my_list = sorted(my_list, key=len) 

# 方法二
import locale
from functools import cmp_to_key
my_list = sorted(my_list, key=cmp_to_key(locale.strcoll)) 

结果如下:

5. 根据另一个列表对列表进行排序

有时,我们可能想要/需要使用一个列表来对另一个列表进行排序。因此,我们将有一个数字列表(索引)和一个我想使用这些索引进行排序的列表。

a = ['blue', 'green', 'orange', 'purple', 'yellow']
b = [3, 2, 5, 4, 1]

sortedList =  [val for (_, val) in sorted(zip(b, a), key=lambda x: x[0])]
print(sortedList)

结果如下:

6. 将列表映射到字典

如果给定一个列表并将其映射到字典中。也就是说,我想将我的列表转换为带有数字键的字典,应该怎么做呢?

mylist = ['blue', 'orange', 'green']
#Map the list into a dict using the map, zip and dict functions
mapped_dict = dict(zip(itr, map(fn, itr)))

字典

与字典相关的2个操作,介绍如下;

7. 合并两个或多个字典

假设我们有两个或多个字典,并且我们希望将它们全部合并为一个具有唯一键的字典。

from collections import defaultdict

def merge_dicts(*dicts):
    mdict = defaultdict(list)
    for dict in dicts:
    for key in dict:
        res[key].append(d[key])
    return dict(mdict)

8. 反转字典

一个非常常见的字典任务是如果我们有一个字典并且想要反转它的键和值。因此,键将成为值,而值将成为键。

当我们这样做时,我们需要确保我没有重复的键,值可以重复,但键不能,并确保所有新键都是可散列的。

my_dict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
# 方法一
my_inverted_dict_1 = dict(map(reversed, my_dict.items()))

# 方法二
from collections import defaultdict
my_inverted_dict_2 = defaultdict(list)
{my_inverted_dict_2[v].append(k) for k, v in my_dict.items()}

print(my_inverted_dict_1)
print(my_inverted_dict_2)

结果如下:

字符串

与字符串相关的3个操作,介绍如下;

9. 使用 f 字符串

格式化字符串可能是您几乎每天都需要完成的第一项任务。在 Python 中有多种方法可以格式化字符串;我最喜欢的是使用 f 字符串。

str_val = 'books'
num_val = 15
print(f'{num_val} {str_val}') 
print(f'{num_val % 2 = }') 
print(f'{str_val!r}') 

price_val = 5.18362
print(f'{price_val:.2f}') 

from datetime import datetime;
date_val = datetime.utcnow()
print(f'{date_val=:%Y-%m-%d}') 

结果如下:

10. 检查子串

我之前需要多次执行的一项非常常见的任务是,检查字符串是否在字符串列表中。

addresses = ["123 Elm Street", "531 Oak Street", "678 Maple Street"]
street = "Elm Street"

# 方法一
for address in addresses:
    if address.find(street) >= 0:
        print(address)

# 方法二
for address in addresses:
    if street in address:
        print(address)

结果如下:

11. 以字节为单位获取字符串的大小

有时,尤其是在构建内存关键应用程序时,我们需要知道我们的字符串使用了多少内存。幸运的是,这可以通过一行代码快速完成。

str1 = "hello"
str2 = "😀"

def str_size(s):
    return len(s.encode('utf-8'))

print(str_size(str1))
print(str_size(str2))

结果如下:

输入/输出操作

与输入/输出操作相关的2个操作,介绍如下;

12. 检查文件是否存在

在数据科学和许多其他应用程序中,我们经常需要从文件中读取数据或向其中写入数据。但要做到这一点,我们需要检查文件是否存在。因此,我们的代码不会因错误而终止。

# 方法一
import os 
exists = os.path.isfile('/path/to/file')

# 方法二
from pathlib import Path
config = Path('/path/to/file') 
if config.is_file(): 
    pass

13.解析电子表格

另一种非常常见的文件交互是从电子表格中解析数据。幸运的是,我们有 CSV 模块来帮助我们有效地执行该任务。

import csv
csv_mapping_list = []
with open("/path/to/data.csv") as my_data:
    csv_reader = csv.reader(my_data, delimiter=",")
    line_count = 0
    for line in csv_reader:
        if line_count == 0:
            header = line
        else:
            row_dict = {key: value for key, value in zip(header, line)}
            csv_mapping_list.append(row_dict)
        line_count += 1

25条实用简洁的python代码

1. 交换两个变量的值


num_1, num_2 = 666, 999
# 一行代码搞定交换两个变量的值
num_1, num_2 = num_2, num_1
print(num_1, num_2)
输出:
999 666

Process finished with exit code 0

2. 查找对象使用的内存

import sys

slogan = "今天你学python了么?"
size = sys.getsizeof(slogan)
print(size)
输出:
100

Process finished with exit code 0

3. 反转字符串

slogan = "今天你学习python了么?"
# 一行代码搞定字符串的反转
new_slogan = slogan[::-1]
print(new_slogan)
输出:
?么了nohtyp习学你天今
Process finished with exit code 0

4. 检查字符串是否为回文

# 定义一个判断字符串是否是回文的函数
def is_palindrome(string):
    return string == string[::-1]

示例:调用判断函数来进行判断slogan是否是回文字符串
slogan = "今天你学python了么?"
_ = is_palindrome(slogan)
print(_)
输出:
False

Process finished with exit code 0

5. 将字符串列表合并为单个字符串

slogan = ["今", "天", "你", "学", "python", "了", "么", "?"]
# 一行代码搞定将字符串列表合并为单个字符串
real_slogan = "".join(slogan)
print(real_slogan)
输出:
今天你学python了么?

Process finished with exit code 0

6. 查找存在于两个列表中任一列表存在的元素

# 定义一个函数用来查找存在于两个列表中任一列表存在的元素
def union(list1, list2):
    return list(set(list1 + list2))

示例:调用该函数用来查找存在于两个列表中任一列表存在的元素
list1, list2 = [5, 2, 0], [5, 2, 1]
new_list = union(list1, list2)
print(new_list)
输出:
[0, 1, 2, 5]

Process finished with exit code 0

7. 打印N次字符串

slogan = "今天你学python了么?"
new_slogan = 11*slogan
print(new_slogan)
输出:
今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?今天你学python了么?

Process finished with exit code 0

8. 链式比较

number = 100
print(98<number<102)
输出:
True

Process finished with exit code 0

print(100==number<102)
输出:
True

Process finished with exit code 0

9. 单词大小写

slogan = "python happy"
# 一行代码搞定单词大小写转换
print(slogan.upper())

# 一行代码搞定单词首字母大写
print(slogan.capitalize())

# 一行代码搞定将每个单词的首字母转为大写,其余小写
print(slogan.title())
输出:
PYTHON HAPPY
Python happy
Python Happy

Process finished with exit code 0

10. 统计列表中元素的频率

from collections import Counter


numbers = [1, 1, 3, 2, 4, 4, 3, 6]
# 一行代码搞定求列表中每个元素出现的频率
count = Counter(numbers)
print(count)
输出:
Counter({1: 2, 3: 2, 4: 2, 2: 1, 6: 1})

Process finished with exit code 0

11. 判断字符串所含元素是否相同

from collections import Counter


course = "python"
new_course = "ypthon"
count_1, count_2 = Counter(course), Counter(new_course)
if count_1 == count_2:
    print("两个字符串所含元素相同!")
输出:
两个字符串所含元素相同!

Process finished with exit code 0

12. 将数字字符串转化为数字列表


string = "666888"
numbers = list(map(int, string))
print(numbers)
输出:
[6, 6, 6, 8, 8, 8]

Process finished with exit code 0

13. 使用enumerate() 函数来获取索引-数值对

string = "python"
for index, value in enumerate(string):
    print(index, value)
输出:
0 p
1 y
2 t
3 h
4 o
5 n

Process finished with exit code 0

14. 代码执行消耗时间

import time

start_time = time.time()

numbers = [i for i in range(10000)]

end_time = time.time()
time_consume = end_time - start_time
print("代码执行消耗的时间是:{}".format(time_consume))
输出示例:
代码执行消耗的时间是:0.002994537353515625

Process finished with exit code 0

15.比较集合和字典的查找效率

import time

number = 999999
# 生成数字列表和数字集合
numbers = [i for i in range(1000000)]
digits = {i for i in range(1000000)}

start_time = time.time()
# 列表的查找
_ = number in numbers
end_time = time.time()

print("列表查找时间为:{}".format(end_time - start_time))

start_time = time.time()
# 集合的查找
_ = number in digits
end_time = time.time()

print("集合查找时间为:{}".format(end_time - start_time))
输出:
列表查找时间为:0.060904741287231445
集合查找时间为:0.0

Process finished with exit code 0

16. 字典的合并

info_1 = {"apple": 13, "orange": 22}
info_2 = {"爆款写作": 48, "跃迁": 49}
# 一行代码搞定合并两个字典
new_info = {**info_1, **info_2}
print(new_info)
输出:
{'apple': 13, 'orange': 22, '爆款写作': 48, '跃迁': 49}
Process finished with exit code 0

17. 随机采样

import random

books = ["爆款写作", "这个世界,偏爱会写作的人", "写作七堂课", "越书写越明白"]
# 随机取出2本书阅读
reading_book = random.sample(books, 2)
print(reading_book)
输出:
['这个世界,偏爱会写作的人', '越书写越明白']

Process finished with exit code 0

18. 判断列表中元素的唯一性

# 定义一个函数判断列表中元素的唯一性
def is_unique(list):
    if len(list) == len(set(list)):
        return True
    else:
        return False


# 调用该函数判断一个列表是否是唯一性的
numbers = [1, 2, 3, 3, 4, 5]
_ = is_unique(numbers)
print(_)
输出:
False
Process finished with exit code 0

19. 计算阶乘 递归函数实现

def fac(n):
    if n > 1:
        return n*fac(n-1)
    else:
        return 1

number = int(input("n="))
print("result = {}".format(fac(number)) )
输出:
n=5
result = 120

Process finished with exit code 0

20. 列出当前目录下的所有文件和目录名

import os


files = [file for file in os.listdir(".")]
print(files)
输出:
['.idea', '2048.py', 'access.log', 'beautiful_girls', 'beautiful_girls_photos', 'boy.py', 'cache.json', 'catoffice.py', 'cookie.json', 'data.csv', 'data.txt', 'diary', 'files', 'filtered_words.txt', 'geckodriver.log', 'get_movies_info2.py', 'girl.py', 'girl1.py', 'ha.conf', 'homework.py', 'homework3.py', 'index.html', 'info.ini', 'notepad.py', 'rent.csv', 'stock.txt', 'student.txt', 'student.xlsx', 'student_register_info.json', 'test.png', 'test_picture.txt', 'zhihu.html', '__pycache__', '九尾1997_200行代码实现2048小游戏.py', '九尾1997_python实现图片转字符画.py', '九尾1997_实现一个简单的计算器.py', '九尾1997_爬取优美图美女写真.py', '九尾1997_爬取北京58租房信息.py', '九尾1997_路飞学城注册页面', '校园管理系统.py', '爬虫模拟登录.py', '记事本.m4a']

Process finished with exit code 0

21. 把原字典的键值对颠倒并生产新的字典

dict_1 = {1: "python", 2: "java"}
new_dict = {value:key for key, value in dict_1.items()}
print(new_dict)
输出:
{'python': 1, 'java': 2}

Process finished with exit code 0

22. 打印九九乘法表

for i in range(1, 10):
    for j in range(1, i+1):
        print("{} * {} = {}".format(i, j, i*j), end="")
    print()
输出:
1 * 1 = 1 
2 * 1 = 2 2 * 2 = 4 
3 * 1 = 3 3 * 2 = 6 3 * 3 = 9 
4 * 1 = 4 4 * 2 = 8 4 * 3 = 12 4 * 4 = 16 
5 * 1 = 5 5 * 2 = 10 5 * 3 = 15 5 * 4 = 20 5 * 5 = 25 
6 * 1 = 6 6 * 2 = 12 6 * 3 = 18 6 * 4 = 24 6 * 5 = 30 6 * 6 = 36 
7 * 1 = 7 7 * 2 = 14 7 * 3 = 21 7 * 4 = 28 7 * 5 = 35 7 * 6 = 42 7 * 7 = 49 
8 * 1 = 8 8 * 2 = 16 8 * 3 = 24 8 * 4 = 32 8 * 5 = 40 8 * 6 = 48 8 * 7 = 56 8 * 8 = 64 
9 * 1 = 9 9 * 2 = 18 9 * 3 = 27 9 * 4 = 36 9 * 5 = 45 9 * 6 = 54 9 * 7 = 63 9 * 8 = 72 9 * 9 = 81 

Process finished with exit code 0

23. 计算每个月天数

import calendar

month_days = calendar.monthrange(2025,8)
print(month_days)
输出:
(4, 31)

Process finished with exit code 0

24. 随机生成验证码,调用随机模块


import random, string

str_1 = "0123456789"
# str_2 是包含所有字母的字符串
str_2 = string.ascii_letters
str_3 = str_1 + str_2
# 多个字符中选取特定数量的字符
verify_code = random.sample(str_3, 6)
# 使用join方法拼接转换为字符串
verify_code = "".join(verify_code)
print(verify_code)
输出:
Mk0L6Y

Process finished with exit code 0

25. 判断闰年

year  = input("请输入一个年份:")
year = int(year)
# 一行代码判断年份是否是闰年
if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
    print("{}是闰年!".format(year))
else:
    print("{}不是闰年!".format(year))
输出示例:
请输入一个年份:2000
2000是闰年!

Process finished with exit code 0

50条有趣的Python一行代码

1、字母异位词

两个单词如果包含相同的字母,次序不同,则称为字母易位词(anagram)。

例如,“silent”和“listen”是字母易位词,而“apple”和“aplee”不是易位词。

from collections import Counter

s1 = 'below'
s2 = 'elbow'

print('anagram') if Counter(s1) == Counter(s2) else print('not an anagram')

使用一行Python代码,就能判断出来了。

2、二进制转十进制

decimal = int('1010', 2)
print(decimal) #10

▍3、将字符串转换为小写

print("Hi my name is XiaoF".lower())
# 'hi my name is xiaof'

print("Hi my name is XiaoF".casefold())
# 'hi my name is xiaof'

▍4、将字符串转换为大写

print("hi my name is XiaoF".upper())
# 'HI MY NAME IS XIAOF'

▍5、将字符串转换为字节

print("convert string to bytes using encode method".encode())
# b'convert string to bytes using encode method'

▍6、拷贝文件

import shutil

shutil.copyfile('source.txt', 'dest.txt')

▍7、快速排序

qsort = lambda l: l if len(l) <= 1 else qsort([x for x in l[1:] if x < l[0]]) + [l[0]] + qsort([x for x in l[1:] if x >= l[0]])

print(qsort([17, 29, 11, 97, 103, 5]))
# [5, 11, 17, 29, 97, 103]

▍8、n个连续数的和

n = 10

print(sum(range(0, n+1)))
# 55

▍9、交换两个变量的值

a,b = b,a

▍10、斐波纳契数列

fib = lambda x: x if x<=1 else fib(x-1) + fib(x-2)

print(fib(20))
# 6765

▍11、将嵌套列表合并为一个列表

main_list = [[0, 1, 2], [11, 12, 13], [52, 53, 54]]

result = [item for sublist in main_list for item in sublist]
print(result)

>
[0, 1, 2, 11, 12, 13, 52, 53, 54]

▍12、运行一个HTTP服务器

python3 -m http.server 8000
python2 -m SimpleHTTPServer

▍13、反转列表

numbers = [0, 1, 2, 11, 12, 13, 52, 53, 54]

print(numbers[::-1])
# [54, 53, 52, 13, 12, 11, 2, 1, 0]

▍14、阶乘

import math

fact_5 = math.factorial(5)
print(fact_5)
# 120

▍15、在列表推导式中使用for和if

even_list = [number for number in [1, 2, 3, 4] if number % 2 == 0]

print(even_list)
# [2, 4]

▍16、列表中最长的字符串

words = ['This', 'is', 'a', 'list', 'of', 'words']

result = max(words, key=len)
print(result)
# 'words'

▍17、列表推导式

li = [num for num in range(0, 10)]

print(li)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

▍18、集合推导式

num_set = {num for num in range(0, 10)}

print(num_set)
# {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

▍19、字典推导式

dict_numbers = {x: x*x for x in range(1, 5)}

print(dict_numbers)
# {1: 1, 2: 4, 3: 9, 4: 16}

▍20、if-else

print("even") if 4 % 2==0 else print("odd")

▍21、无限循环

while 1:0

▍22、检查数据类型

print(isinstance(2, int))
# True

print(isinstance("allwin", str))
# True

print(isinstance([3, 4, 1997], list))
# True

▍23、While循环

a = 5

while a > 0:
    a = a - 1

print(a)
# 0

▍24、使用print语句写入文件

print("Hello, World!", file=open('file.txt', 'w'))

既可打印出信息,还能将信息保存文件。

▍25、计算一个字符在字符串中出现的频率

print("umbrella".count('l'))
# 2

▍26、合并列表

list1 = [1, 2, 4]
list2 = ['XiaoF']
list1.extend(list2)

print(list1)
# [1, 2, 4, 'XiaoF']

▍27、合并字典

dict1 = {'name': 'weiwei', 'age': 23}
dict2 = {'city': 'Beijing'}
dict1.update(dict2)

print(dict1)
# {'name': 'weiwei', 'age': 23, 'city': 'Beijing'}

▍28、合并集合

set1 = {0, 1, 2}
set2 = {11, 12, 13}
set1.update(set2)

print(set1)
# {0, 1, 2, 11, 12, 13}

▍29、时间戳

import time

print(time.time())

▍30、列表中出现次数最多的元素

test_list = [9, 4, 5, 4, 4, 5, 9, 5, 4]
most_frequent_element = max(set(test_list), key=test_list.count)

print(most_frequent_element)
# 4

▍31、嵌套列表

numbers = [[num] for num in range(10)]

print(numbers)
# [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]

▍32、八进制转十进制

print(int('30', 8)) 
# 24

▍33、将键值对转换为字典

result = dict(name='XiaoF', age=23)

print(result)
# {'name': 'XiaoF', 'age': 23}

▍34、求商和余数

quotient, remainder = divmod(4, 5)

print(quotient, remainder)
# 0 4

divmod()函数返回当参数1除以参数2时,包含商和余数的元组。

▍35、删除列表中的重复项

print(list(set([4, 4, 5, 5, 6])))
# [4, 5, 6]

▍36、按升序排序列表

print(sorted([5, 2, 9, 1]))
# [1, 2, 5, 9]

▍37、按降序排序列表

print(sorted([5, 2, 9, 1], reverse=True))
# [9, 5, 2, 1]

▍38、获取小写字母表

import string

print(string.ascii_lowercase)
# abcdefghijklmnopqrstuvwxyz

▍39、获取大写字母表

import string

print(string.ascii_uppercase)
# ABCDEFGHIJKLMNOPQRSTUVWXYZ

▍40、获取0到9字符串

import string

print(string.digits)
# 0123456789

▍41、十六进制转十进制

print(int('da9', 16))
# 3497

▍42、日期时间

import time

print(time.ctime())
# Thu Aug 13 20:00:00 2021

▍43、将列表中的字符串转换为整数

print(list(map(int, ['1', '2', '3'])))
# [1, 2, 3]

▍44、用键对字典进行排序

d = {'one': 1, 'four': 4, 'eight': 8}
result = {key: d[key] for key in sorted(d.keys())}

print(result)
# {'eight': 8, 'four': 4, 'one': 1}

▍45、用键值对字典进行排序

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
result = {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}

print(result)
# {0: 0, 2: 1, 1: 2, 4: 3, 3: 4}

▍46、列表旋转

li = [1, 2, 3, 4, 5]

# li[n:] + li[:n], 右变左
print(li[2:] + li[:2])
# [3, 4, 5, 1, 2]

# li[-n:] + li[:-n], 左变右
print(li[-1:] + li[:-1])
# [5, 1, 2, 3, 4]

▍47、将字符串中的数字移除

message = ''.join(list(filter(lambda x: x.isalpha(), 'abc123def4fg56vcg2')))

print(message)
# abcdeffgvcg

▍48、矩阵变换

old_list = [[1, 2, 3], [3, 4, 6], [5, 6, 7]]
result = list(list(x) for x in zip(*old_list))

print(result)
# [[1, 3, 5], [2, 4, 6], [3, 6, 7]]

▍49、列表过滤

result = list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6]))

print(result)
# [2, 4, 6]

▍50、解包

a, *b, c = [1, 2, 3, 4, 5]

print(a) # 1
print(b) # [2, 3, 4]
print(c) # 5

如何杀死一个Python线程

1. 线程无法结束

A Threaded Example

  • 下面是一个简单的,多线程的示例代码。
import random
import threading
import time

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())  # do some work...
    print(f'{i} iterations completed before exiting.')

th = threading.Thread(target=bg_thread)
th.start()
th.join()
  • 使用下面命令来运行程序,在下面的程序运行中,当跑到第 7 次迭代时,按下 Ctrl-C 来中断程序,发现后台运行的程序并没有终止掉。而在第 13 次迭代时,再次按下 Ctrl-C 来中断程序,发现程序真的退出了。
$ python thread.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
7 of 30 iterations...
^CTraceback (most recent call last):
  File "thread.py", line 14, in <module>
    th.join()
  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join
    self._wait_for_tstate_lock()
  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt
8 of 30 iterations...
9 of 30 iterations...
10 of 30 iterations...
11 of 30 iterations...
12 of 30 iterations...
13 of 30 iterations...
^CException ignored in: <module 'threading' from '/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py'>
Traceback (most recent call last):
  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1388, in _shutdown
    lock.acquire()
KeyboardInterrupt:
  • 这很奇怪,不是吗?究其原因是,Python 有一些逻辑是会在进程退出前运行的,专门用来等待任何没有被配置为守护线程的后台线程结束,然后再把控制权真正交给操作系统。因此,该进程在其主线程运行时收到到了中断信号,并准备退出。首先,它需要等待后台线程运行结束。但是,这个线程对中断一无所知,这个线程只知道它需要在运行结束前完成 30 次迭代。
  • Python 在退出过程中使用的等待机制有一个规定,当收到第二个中断信号时,就会中止。这就是为什么第二个 Ctrl-C 会立即结束进程。所以我们看到了,线程是不能被杀死!在下面的章节中,将向展示 Python 中的两个方式,来使线程及时结束。

2. 使用守护进程

Daemon Threads

  • 在上面提到过,在 Python 退出之前,它会等待任何非守护线程的线程。而守护线程就是,一个不会阻止 Python 解释器退出的线程。
  • 如何使一个线程成为一个守护线程?所有的线程对象都有一个 daemon 属性,可以在启动线程之前将这个属性设置为 True,然后该线程就会被视为一个守护线程。下面是上面的示例应用程序,修改后守护线程版本:
import random
import threading
import time

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())  # do some work...
    print(f'{i} iterations completed before exiting.')

th = threading.Thread(target=bg_thread)
th.daemon = True
th.start()
th.join()
  • 再次运行它,并尝试中断它,发现第一个执行 Ctrl-C 后进程立即就退出了。
~ $ python x.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
^CTraceback (most recent call last):
  File "thread.py", line 15, in <module>
    th.join()
  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join
    self._wait_for_tstate_lock()
  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt
  • 那么这个线程会发生什么呢?线程继续运行,就像什么都没发生一样,直到 Python 进程终止并返回到操作系统。这时,线程就不存在了。你可能认为这实际上是一种杀死线程的方法,但要考虑到以这种方式杀死线程,你必须同时杀死进程。

3. 使用事件对象

Python Events

  • 使用守护线程,是一种避免在多线程程序中处理意外中断的简单方法,但这是一种只在进程退出的特殊情况下才有效的技巧。不幸的是,有些时候,一个应用程序可能想结束一个线程而不必杀死自己。另外,有些线程可能需要在退出前执行清理工作,而守护线程则不允许这样操作。
  • 那么,还有什么其他选择呢?既然不可能强制线程结束,那么唯一的选择就是给它添加逻辑,让它在被要求退出时自愿退出。有多种方法都可以解决上述问题,但我特别喜欢的一种方法,就是使用一个 Event 对象。

Event 类是由 Python 标准库的线程模块提供,你可以通过实例化类来创建一个事件对象,就像下面这个样子:

exit_event = threading.Event()
  • Event 对象可以处于两种状态之一: setnot set。当我们实例化创建之后,默认事件并没有被设置。

    • 若要将事件状态更改为 set,则可以调用 set()方法;
    • 要查明是否设置了事件,使用 is_set() 方法,设置了则返回 True;
    • 还可以使用 wait() 方法等待事件,等待操作阻塞直到设置事件(可以设置超时)
  • 其核心思路,就是在线程需要退出的时候设置事件。然后,线程需要经常地检查事件的状态(通常是在循环中),并在发现事件已经设置时处理自己的终止。对于上面显示的示例,一个好的解决方案是添加一个捕获 Ctrl-C 中断的信号处理程序,而不是突然退出,只需设置事件并让线程优雅地结束。

import random
import signal
import threading
import time

exit_event = threading.Event()

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())  # do some work...
        if exit_event.is_set():
            break
    print(f'{i} iterations completed before exiting.')

def signal_handler(signum, frame):
    exit_event.set()

signal.signal(signal.SIGINT, signal_handler)
th = threading.Thread(target=bg_thread)
th.start()
th.join()
  • 如果你尝试中断这个版本的应用程序,一切看起来都会更好:
$ python thread.py
1 of 30 iterations...
2 of 30 iterations...
3 of 30 iterations...
4 of 30 iterations...
5 of 30 iterations...
6 of 30 iterations...
7 of 30 iterations...
^C7 iterations completed before exiting.
  • 需要注意的是,中断是如何被优雅地处理的,以及线程能够运行在循环之后出现的代码。如果当线程需要在退出之前,关闭文件句柄或数据库连接时,这种方式就非常有用了。其能够在线程退出之前,运行清理代码有时是必要的,以避免资源泄漏。我在上面提到过,event 对象也是可以等待的:
for i in range(1, 30):
    print(f'{i} of 30 iterations...')
    time.sleep(random.random())

    if exit_event.is_set():
        break
  • 在每个迭代中,都有一个对 time.sleep() 的调用,这将阻塞线程。如果在线程 sleep 时设置了退出事件,那么它就不能检查事件的状态,因此在线程能够退出之前会有一个小的延迟。在这种情况下,如果有 sleep,使用 wait() 方法将 sleepevent 对象的检查结合起来会更有效:
 for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        if exit_event.wait(timeout=random.random()):
            break
  • 这个解决方案有效地为提供了一个可中断的 sleep,因为在线程停留在 wait() 调用的中间时设置了事件,那么等待将立即返回。

4. 总结陈述说明

Conclusion

  • 你知道 Python 中的 event 对象吗?它们是比较简单的同步原语之一,不仅可以用作退出信号,而且在线程需要等待某些外部条件发生的许多其他情况下也可以使用。

24 个 Python 实用技巧

all or any

Python 语言如此流行的众多原因之一,是因为它具有很好的可读性和表现力。

人们经常开玩笑说 Python 是可执行的伪代码。当你可以像这样写代码时,就很难反驳。

x = [True, True, False]
if any(x):
    print("至少有一个True")
if all(x):
    print("全是True")
if any(x) and not all(x):
    print("至少一个True和一个False")

bashplotlib

你有没有想过在控制台中绘制图形吗?

Bashplotlib 是一个 Python 库,他能够帮助我们在命令行(粗旷的环境)中绘制数据。

# 模块安装
pip install bashplotlib
# 绘制实例
import numpy as np
from bashplotlib.histpgram import plot_hist
arr = np.ramdom.normal(size=1000, loc=0, scale=1)
plot_hist(arr, bincount=50)

collections

Python 有一些很棒的默认数据类型,但有时它们的行为并不完全符合你的期望。

幸运的是,Python 标准库提供了 collections 模块[1]。这个方便的附加组件为你提供了更多的数据类型。

from collections import OrderedDict, Counter
# 记住键的添加顺序!
x = OrderedDict(a=1, b=2, c=3)
# 统计每个字符出现的频率
y = Counter("Hello World!")

dir

有没有想过如何查看 Python 对象内部并查看它具有哪些属性?在命令行中输入:

dir() 
dir("Hello World") 
dir(dir)

当以交互方式运行 Python 以及动态探索你正在使用的对象和模块时,这可能是一个非常有用的功能。在这里阅读更多functions[2]相关内容。

emoji

emoji[3] 是日本在无线通信中所使用的视觉情感符号,绘指图画,文字指的则是字符,可用来代表多种表情,如笑脸表示笑、蛋糕表示食物等。在中国大陆,emoji通常叫做“小黄脸”,或者直称emoji。

# 安装模块
pip install emoji
# 做个尝试
from emoji import emojize
print(emojize(":thumbs_up:"))

👍

from future import

Python 流行的结果之一,总是有新版本正在开发中。新版本意味着新功能 —— 除非你的版本已过时。

不过不要担心。使用该future模块[4]可以帮助你用Python的未来版本导入功能。从字面上看,这就像时间旅行、魔法或其他东西。

from __future__ import print_function
print("Hello World!")

geogy

地理,对大多数程序员来说是一个具有挑战性的领域。在获取地理信息或者绘制地图时,也会遇到不少问题。这个geopy 模块[5]让地理相关内容变得非常容易。

pip install geopy

它通过抽象一系列不同地理编码服务的 API 来工作。通过它,你能够获得一个地方的完整街道地址、纬度、经度甚至海拔高度。

还有一个有用的距离类。它以你偏好的测量单位计算两个位置之间的距离。

from geopy import GoogleV3
place = "221b Baker Street, London"
location = GoogleV3().geocode(place)
print(location.address)
print(location.location)

howdoi

当你使用terminal终端编程时,通过在遇到问题后会在StackOverflow上搜索答案,完后会回到终端继续编程,此时有时会不记得你之前查到的解决方案,此时需要重新查看StackOverflow,但又不想离开终端,那么此时你需要用到这个有用的命令行工具howdoi[6]。

pip install howdoi

无论你有什么问题,都可以问它,它会尽力回复。

howdoi vertical align css
howdoi for loop in java
howdoi undo commits in git

但请注意——它会从 StackOverflow 的最佳答案中抓取代码。它可能并不总是提供最有用的信息……

howdoi exit vim

inspect

Python 的inspect模块[7]非常适合了解幕后发生的事情。你甚至可以调用它自己的方法!

下面的代码示例inspect.getsource() 用于打印自己的源代码。 inspect.getmodule() 还用于打印定义它的模块。

最后一行代码打印出它自己的行号。

import inspect
print(inspect.getsource(inspect.getsource))
print(inspect.getmodule(inspect.getmodule))
print(inspect.currentframe().f_lineno)

当然,除了这些微不足道的用途,inspect 模块可以证明对理解你的代码在做什么很有用。你还可以使用它来编写自文档化代码。

Jedi

Jedi 库是一个自动完成和代码分析库。它使编写代码更快、更高效。

除非你正在开发自己的 IDE,否则你可能对使用Jedi [8]作为编辑器插件比较感兴趣。幸运的是,这已经有可用的负载!

**kwargs

在学习任何语言时,都会有许多里程碑。使用 Python 并理解神秘的**kwargs语法可能算作一个重要的里程碑。

字典对象前面的双星号\kwargs[9]允许你将该字典的内容作为命名参数传递给函数。

字典的键是参数名称,值是传递给函数的值。你甚至不需要调用它kwargs

dictionary = {"a": 1, "b": 2}
def someFunction(a, b):
    print(a + b)
    return
# 这些做同样的事情:
someFunction(**dictionary)
someFunction(a=1, b=2)

当你想编写可以处理未预先定义的命名参数的函数时,这很有用。

列表(list)推导式

关于 Python 编程,我最喜欢的事情之一是它的列表推导式[10]。

这些表达式可以很容易地编写非常顺畅的代码,几乎与自然语言一样。

numbers = [1,2,3,4,5,6,7]
evens = [x for x in numbers if x % 2 is 0]
odds = [y for y in numbers if y not in evens]
cities = ['London', 'Dublin', 'Oslo']

def visit(city):
    print("Welcome to "+city)

for city in cities:
    visit(city)

map

Python 通过许多内置功能支持函数式编程。最有用的map()功能之一是函数——尤其是与lambda 函数[11]结合使用时。

x = [1, 2, 3] 
y = map(lambda x : x + 1, x)
# 打印出 [2,3,4]
print(list(y))

在上面的示例中,map()将一个简单的 lambda 函数应用于x. 它返回一个映射对象,该对象可以转换为一些可迭代对象,例如列表或元组。

newspaper3k

如果你还没有看过它,那么准备好被Python newspaper module [12]模块震撼到。它使你可以从一系列领先的国际出版物中检索新闻文章和相关的元数据。你可以检索图像、文本和作者姓名。它甚至有一些内置的 NLP 功能[13]。

因此,如果你正在考虑在下一个项目中使用 BeautifulSoup 或其他一些 DIY 网页抓取库,使用本模块可以为你自己节省不少时间和精力。

pip install newspaper3k

Operator overloading

Python 提供对运算符重载的[14]支持,这是让你听起来像一个合法的计算机科学家的术语之一。

这实际上是一个简单的概念。有没有想过为什么 Python 允许你使用+运算符来添加数字以及连接字符串?这就是操作符重载的作用。

你可以定义以自己的特定方式使用 Python 的标准运算符符号的对象。并且你可以在与你正在使用的对象相关的上下文中使用它们。

class Thing:
    def __init__(self, value):
        self.__value = value
    def __gt__(self, other):
        return self.__value > other.__value
    def __lt__(self, other):
        return self.__value < other.__value
something = Thing(100)
nothing = Thing(0)
# True
something > nothing
# False
something < nothing
# Error
something + nothing

pprint

Python 的默认print函数完成了它的工作。但是如果尝试使用print函数打印出任何大的嵌套对象,其结果相当难看。这个标准库的漂亮打印模块pprint[15]可以以易于阅读的格式打印出复杂的结构化对象。

这算是任何使用非平凡数据结构的 Python 开发人员的必备品。

import requests
import pprint
url = 'https://randomuser.me/api/?results=1'
users = requests.get(url).json()
pprint.pprint(users)

Queue

Python 标准库的 Queue 模块实现支持多线程。这个模块让你实现队列数据结构。这些是允许你根据特定规则添加和检索条目的数据结构。

“先进先出”(FIFO)队列让你可以按添加顺序检索对象。“后进先出”(LIFO) 队列让你可以首先访问最近添加的对象。

最后,优先队列让你可以根据对象的排序顺序检索对象。

这是一个如何在 Python 中使用队列Queue[16]进行多线程编程的示例。

repr

在 Python 中定义类或对象时,提供一种将该对象表示为字符串的“官方”方式很有用。例如:

>>> file = open('file.txt', 'r') 
>>> print(file) 
<open file 'file.txt', mode 'r' at 0x10d30aaf0>

这使得调试代码更加容易。将其添加到你的类定义中,如下所示:

class someClass: 
    def __repr__(self): 
        return "<some description here>"
someInstance = someClass()
# 打印 <some description here>
print(someInstance)

sh

Python 是一种很棒的脚本语言。有时使用标准的 os 和 subprocess 库可能有点头疼。

SH库[17]让你可以像调用普通函数一样调用任何程序——对于自动化工作流和任务非常有用。

import sh
sh.pwd()
sh.mkdir('new_folder')
sh.touch('new_file.txt')
sh.whoami()
sh.echo('This is great!')

Type hints

Python 是一种动态类型语言。定义变量、函数、类等时不需要指定数据类型。这允许快速的开发时间。但是,没有什么比由简单的输入问题引起的运行时错误更烦人的了。

Python 3.5[18] 开始,你可以选择在定义函数时提供类型提示。

def addTwo(x : Int) -> Int:
    return x + 2

你还可以定义类型别名。

from typing import List
Vector = List[float]
Matrix = List[Vector]
def addMatrix(a : Matrix, b : Matrix) -> Matrix:
  result = []
  for i,row in enumerate(a):
    result_row =[]
    for j, col in enumerate(row):
      result_row += [a[i][j] + b[i][j]]
    result += [result_row]
  return result
x = [[1.0, 0.0], [0.0, 1.0]]
y = [[2.0, 1.0], [0.0, -2.0]]
z = addMatrix(x, y)

尽管不是强制性的,但类型注释可以使你的代码更易于理解。

它们还允许你使用类型检查工具,在运行前捕获那些杂散的 TypeError。如果你正在处理大型、复杂的项目,这是很有用的!

uuid

通过Python 标准库的 uuid 模块[19]生成通用唯一 ID(或“UUID”)的一种快速简便的方法。

import uuid
user_id = uuid.uuid4()
print(user_id)

这将创建一个随机的 128 位数字,该数字几乎肯定是唯一的。事实上,可以生成超过 2¹²² 种可能的 UUID。这超过了五个十进制 (或 5,000,000,000,000,000,000,000,000,000,000,000,000)。

在给定的集合中发现重复的概率极低。即使有一万亿个 UUID,重复存在的可能性也远低于十亿分之一。

Virtual environments

你可能同时在多个 Python 项目上工作。不幸的是,有时两个项目将依赖于相同依赖项的不同版本。你在你的系统上安装了什么?

幸运的是,Python支持对 虚拟环境[20] 的让你可以两全其美。从命令行:

python -m venv my-project 
source my-project/bin/activate 
pip install all-the-modules

现在,你可以在同一台机器上运行 Python 的独立版本和安装。

wikipedia

维基百科有一个很棒的 API,它允许用户以编程方式访问无与伦比的完全免费的知识和信息。在wikipedia模块[21]使访问该API非常方便。

import wikipedia
result = wikipedia.page('freeCodeCamp')
print(result.summary)
for link in result.links:
    print(link)

和真实站点一样,该模块提供了多语言支持、页面消歧、随机页面检索,甚至还有一个donate()方法。

xkcd

幽默是 Python 语言的一个关键特征,它是以英国喜剧小品剧Python飞行马戏团[22]命名的。Python 的许多官方文档都引用了该节目最著名的草图。不过,Python 的幽默并不仅限于文档。试试运行下面的行:

import antigravity

YAML

YAML[23]指的是 “ 非标记语言” 。它是一种数据格式化语言,是 JSON 的超集。

与 JSON 不同,它可以存储更复杂的对象并引用它自己的元素。你还可以编写注释,使其特别适合编写配置文件。该PyYAML模块[24]可让你使用YAML使用Python。

安装并然后导入到你的项目中:

pip install pyyaml

import yaml

PyYAML 允许你存储任何数据类型的 Python 对象,以及任何用户定义类的实例。

zip

压轴出场的也是很棒的一个模块。你曾经遇到过需要从两个列表中形成字典吗?

keys = ['a', 'b', 'c']
vals = [1, 2, 3]
zipped = dict(zip(keys, vals))

zip()内置函数需要一系列可迭代的对象,并返回一个元组列表中。每个元组按位置索引对输入对象的元素进行分组。

你还可以通过调用对象来“解压缩”对象*zip()

参考资料

[1] collections 模块: https://docs.python.org/3/library/collections.html

[2] functions: https://docs.python.org/3/library/functions.html#dir

[3] emoji: https://pypi.org/project/emoji/

[4] future模块: https://docs.python.org/2/library/future.html

[5] geopy 模块: https://geopy.readthedocs.io/en/latest/

[6] howdoi: https://github.com/gleitz/howdoi

[7] inspect模块: https://docs.python.org/3/library/inspect.html

[8] Jedi : https://jedi.readthedocs.io/en/latest/docs/usage.html

[9] *kwargs: *https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments

[10] 列表推导式: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

[11] lambda 函数: https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions

[12] Python newspaper module : https://pypi.org/project/newspaper3k/

[13] 内置的 NLP 功能: https://newspaper.readthedocs.io/en/latest/user_guide/quickstart.html#performing-nlp-on-an-article

[14] 运算符重载的: https://docs.python.org/3/reference/datamodel.html#special-method-names

[15] pprint: https://docs.python.org/3/library/pprint.html

[16] Queue: https://www.tutorialspoint.com/python3/python_multithreading.htm

[17] SH库: http://amoffat.github.io/sh/

[18] Python 3.5: https://docs.python.org/3/library/typing.html

[19] uuid 模块: https://docs.python.org/3/library/uuid.html

[20] 虚拟环境: https://docs.python.org/3/tutorial/venv.html

[21] wikipedia模块: https://wikipedia.readthedocs.io/en/latest/quickstart.html

[22] Python飞行马戏团: https://en.wikipedia.org/wiki/Monty_Python's_Flying_Circus

[23] YAML: http://yaml.org/

[24] PyYAML模块: https://pyyaml.org/wiki/PyYAMLDocumentation

[25] awesome-python: https://awesome-python.com/

45 道 Python 练习题

题目预览
1.如何用 Python 输出一个 100 以内的斐波那契数列(Fibonacci* sequence)?★★☆☆☆
2.什么是 lambda 函数?它有什么好处?★★☆☆☆
3.请简述 Pythonis== 的区别。★★☆☆☆
4.请简述 function(*args, **kwargs) 中的 *args, **kwargs 分别是什么意思?★★☆☆☆
5.请简述面向对象中 __new____init__ 的区别。★★★☆☆
6.Python 子类继承自多个父类时,如多个父类有同名方法,子类将继承自哪个方法?★☆☆☆☆
7.判断两个字符串是否同构。★★☆☆☆
8.请写出匹配中国大陆手机号且结尾不是 4 和 7 的正则表达式。★★☆☆☆
9.使用递推式将矩阵 [ [1, 2], [3, 4], [5, 6] ] 转换为一维向量。★☆☆☆☆
10.编写 Python 程序,打印星号金字塔。★★☆☆☆
11.生成随机整数、随机小数、0-1之间小数的方法。★★☆☆☆
12.列表 [1, 2, 3, 4, 5],使用 map() 函数输出 [1, 4, 9, 16, 25],再通过列表推导式获取大于 10 的数。★★☆☆☆
13.对字符串去重,并从大到小输出排序。★★☆☆☆
14.字典根据键从大到小排序;根据值从大到小排序。★★☆☆☆
15.统计字符串中每个字母出现的次数,获取出现次数最多的两个字母及它们的出现次数。★★★☆☆
16.filter 方法获取 1 - 10 中 3 的倍数。★★☆☆☆
17.a = (1),b = (1, ),c = (‘1’)分别是什么类型。★★☆☆☆
18.x = ‘abc’, y = ‘def’, z = [‘d’, ‘e’, ‘f’],写出 x.join(y) , x.join(z) 的结果。★★☆☆☆
19.s = ‘静香语文100分, 静香数学100分’,将第一个 “静香” 换成 “胖虎” 。★★☆☆☆
20.写出常见的几种异常及含义。★★★☆☆
21.解释一下直接赋值、浅拷贝与深拷贝有什么区别?★★★★☆
22.使用 lambda 函数对 lst = [-1, -5, 2, -3, 1, 4, -4, 3, -2, 5] 进行排序,要求排序结果正数在前(从小到大),负数在后(从大到小),如 [1, 2, 3, 4, 5, -1, -2, -3, -4, -5]。★★★☆☆
23.按照年龄从小到大,班级从小到大对 [{‘age’: 18, ‘class’: 2}, {‘age’: 21, ‘class’: 1}, {‘age’: 19, ‘class’: 2}, {‘age’: 19, ‘class’: 1}] 列表进行排序。★★★☆☆
24.Python 中有哪些可变类型和不可变类型,它们有什么不同?★★☆☆☆
25.写出你知道的将两个字典合并为一个新字典的方法。★★★★☆
26.将文件内容按行读取到列表中。★★☆☆☆
27.Python 内置函数 any()all() 的用法。★★☆☆☆
28.谈谈 Python 中的垃圾回收机制。★★★★☆
29.Pythonre 模块中 match()search() 的区别?★★★☆☆
30.在读文件操作的时候会使用 read()readline() 或者 readlines(),简述它们各自的作用。★★★☆☆
31.解释一下什么是闭包?★★☆☆☆
32.递归函数停止的条件。★★☆☆☆
33.Python 正则匹配 “good good study, day day up” 时,(g.*d)(g.*?d) 有什么区别?★★★☆☆
34.简述解释型和编译型编程语言?★★★☆☆
35.xrangerange 的区别?★★☆☆☆
36.简述 yieldyield from 关键字。★★★★☆
37.Python 中的 help()dir() 函数有什么用?★★★☆☆
38.在 Python 中怎么实现线程?★★★★☆
39.Python 中什么是猴子补丁?★★★★☆
40.如何以就地操作方式打乱一个列表的元素?★★☆☆☆
41.将字符串 'love' 更改为 'live'。★★☆☆☆
42.写出下列代码的结果。★★☆☆☆
43.Python 中函数调用参数的传递方式是值传递还是引用传递?★★★☆☆
44.现有一个 DataFrame 数据集中包含学生的姓名与分数,先要新增一列”评价”记录学生的及格情况,如果分数低于 60 即为 “不及格”,否则 “及格”。★★★☆☆
45.现在为获取更详细的信息,需要将 “评价” 列进行划分,60以下不及格、60至80及格、80以上优秀。★★★☆☆

Test 1

如何用 Python 输出一个 100 以内的斐波那契数列(Fibonacci sequence)?难度:★★☆☆☆

>>> a ,b = 0, 1
>>> while b < 100:
        print(b, end=' ')
        a, b = b, a + b


1 1 2 3 5 8 13 21 34 55 89

Test 2

什么是 lambda 函数?它有什么好处?

难度:★★☆☆☆

lambda 函数是一个可以接收任意多个参数 (包括可选参数) 并且返回单个表达式值的函数。lambda 函数不能包含命令,它们所包含的表达式不能超过一个。

Test 3

请简述 Python 中 is== 的区别。

难度:★★☆☆☆

Python 中的对象包含三个要素:idtypevalueis 比较的是两个对象的 id== 比较的是两个对象的 value

Test 4

请简述 function(*args, **kwargs) 中的 *args, **kwargs 分别是什么意思?

难度:★★☆☆☆

*args**kwargs 主要用于函数定义的参数。Python 语言允许将不定数量的参数传给一个函数,其中

  • *args 表示一个非键值对的可变参数列表;
  • **kwargs 则表示不定数量的键值对参数列表。

注意:*args**kwargs 可以同时在函数的定义中,但是 *args 必须在**kwargs 前面。

Test 5

请简述面向对象中 __new____init__ 的区别。

难度:★★★☆☆

(1) __new__ 至少要有一个参数 cls,代表当前类,此参数在实例化时由 Python 解释器自动识别。

(2) __new__ 返回生成的实例,可以返回父类(通过 super (当前类名, cls )的方式)__new__出来的实例, 或者直接是对象的 __new__ 出来的实例。这在自己编程实现 __new__ 时要特别注意。

(3) __init__ 有一个参数 self,就是这个 __new__ 返回的实例, __init____new__ 的基础上可以完成一些其它初始化的动作, __init__ 不需要返回值。

(4) 如果 __new__ 创建的是当前类的实例,会自动调用 __init__ ,通过返回语句里面调用的 __new__ 函 数的第一个参数是 cls 来保证是当前类实例,如果是其他类的类名,那么实际创建并返回的就是其他类的实例,也就不会调用当前类或其他类的 __init__ 函数。

Test 6

Python 子类继承自多个父类时,如多个父类有同名方法,子类将继承自哪个方法?

难度:★☆☆☆☆

Python 语言中子类继承父类的方法是按照继承的父类的先后顺序确定的,例如,子类 A 继承自父类 B、 C,且 B、C 中具有同名方法 Test() ,那么 A 中的 Test() 方法实际上是继承自B中的 Test() 方法。

Test 7

判断两个字符串是否同构。

难度:★★☆☆☆

字符串同构是指字符串 s 中的所有字符都可以替换为 t 中的所有字符。在保留字符顺序的同时,必须用另 一个字符替换所有出现的字符。不能将 s 中的两个字符映射到 t 中同一个字符,但字符可以映射到自身。试判定给定的字符串 s 和 t 是否同构。例如:

s = "paper"
t = "title"
out:True
s = "add" 
t = "apple" 
out:False
>>> s = 'app'
>>> t = 'add'
>>> len(set(s)) == len(set(t)) == len(set(zip(s, t)))
True

Test 8

请写出匹配中国大陆手机号且结尾不是 4 和 7 的正则表达式。

难度:★★☆☆☆

>>> import re
>>> tel = '15674899231'
>>> "有效" if (re.match(r"1\d{9}[0-3,5-6,8-9]", tel) != None) else "无效"
'有效'

Test 9

使用递推式将矩阵 [ [1, 2], [3, 4], [5, 6] ] 转换为一维向量。

难度:★☆☆☆☆

>>> a = [[1, 2], [3, 4], [5, 6]]
>>> [j for i in a for j in i]
[1, 2, 3, 4, 5, 6]

Test 10

编写 Python 程序,打印星号金字塔。

难度:★★☆☆☆

编写尽量短的 Python 程序,实现打印星号金字塔。例如 n=5 时输出以下金字塔图形:

>>> n = 5
>>> for i in range(1, n + 1):
 print(' '*(n-(i-1))+'*'*(2*i-1))


     *
    ***
   *****
  *******
 *********

Test 11

生成随机整数、随机小数、0-1之间小数的方法。

难度:★★☆☆☆

>>> import random
>>> import numpy as np
>>> print('正整数:', random.randint(1, 10))
正整数: 2

>>> print('5个随机小数:', np.random.randn(5))
5个随机小数: [ 0.76768263 -0.3587897   0.04880354 -0.02443411  0.73785606]

>>> print('0-1随机小数:', random.random())
0-1随机小数: 0.24387235868905555

Test 12

列表 [1, 2, 3, 4, 5],使用 map() 函数输出 [1, 4, 9, 16, 25],再通过列表推导式获取大于 10 的数。

难度:★★☆☆☆

>>> lst = [1, 2, 3, 4, 5]
>>> map(lambda x: x**2, lst)
<map object at 0x000002B37C9D88B0>

>>> [n for n in res if n > 10]
[16, 25]

Test 13

对字符串去重,并从大到小输出排序。

难度:★★☆☆☆

>>> s = 'aabcddeff'
>>> set_s = set(list(s))
>>> list_s = sorted(list(set_s), reverse=True)
>>> ''.join(list_s)
'fedcba'

Test 14

字典根据键从大到小排序;根据值从大到小排序。

难度:★★☆☆☆

dic = {'a': 2, 'b': 1, 'c': 3, 'd': 0}
lst1 = sorted(dic.items(), key=lambda x: x[0], reverse=False)
# [('a', 2), ('b', 1), ('c', 3), ('d', 0)]
lst2 = sorted(dic.items(), key=lambda x: x[1], reverse=False)
# [('d', 0), ('b', 1), ('a', 2), ('c', 3)]
res_dic1 = {key: value for key, value in lst1}
res_dic2 = {key: value for key, value in lst2}
print('按照键降序:', res_dic1)
print('按照值降序:', res_dic2)

# 按照键降序: {'a': 2, 'b': 1, 'c': 3, 'd': 0}
# 按照值降序: {'d': 0, 'b': 1, 'a': 2, 'c': 3}

Test 15

统计字符串中每个字母出现的次数,获取出现次数最多的两个字母及它们的出现次数。

难度:★★★☆☆

>>> from collections import Counter
>>> s = 'aaaabbbccd'
>>> Counter(s).most_common(2)
[('a', 4), ('b', 3)]

Test 16

filter() 方法获取 1 - 10 中 3 的倍数。

难度:★★☆☆☆

>>> list(filter(lambda x: x % 3 == 0, list(range(1, 10))))
[3, 6, 9]

Test 17

a = (1),b = (1, ),c = ('1')分别是什么类型。

难度:★★☆☆☆

>>> type((1))
<class 'int'>

>>> type((1,))
<class 'tuple'>

>>> type(('1'))
<class 'str'>

Test 18

x = 'abc', y = 'def', z = ['d', 'e', 'f'],写出 x.join(y) , x.join(z) 的结果。

难度:★★☆☆☆

>>> x = 'abc'
>>> y = 'def'
>>> z = list(y)
>>> x.join(y)
'dabceabcf'

>>> x.join(z)
'dabceabcf'

Test 19

s = ‘静香语文100分, 静香数学100分’,将第一个 “静香” 换成 “胖虎” 。

难度:★★☆☆☆

>>> s = '静香语文100分, 静香数学100分'
>>> s.replace('静香', '胖虎', 1)
'胖虎语文100分, 静香数学100分'

Test 20

写出常见的几种异常及含义。

难度:★★★☆☆

  • IOError :输入输出异常
  • AttributeError :试图访问一个对象没有的属性
  • ImportError :无法引入模块或包 / 对象
  • IndentationError :代码缩进错误
  • IndexError :下标索引超出边界
  • KeyError :试图访问映射中不存在的键
  • SyntaxError :Python语法错误
  • NameErrir :未声明/初始化对象 (没有属性)
  • TabError :Tab和空格混用
  • ValueError :传入无效的参数
  • OverflowError :数值运算超出最大限制

Test 21

解释一下直接赋值、浅拷贝与深拷贝有什么区别?

难度:★★★★☆

(1)直接赋值,传递对象的引用而已。原始列表改变,被赋值的对象也会做相同改变。

(2)浅拷贝,没有拷贝子对象,所以原始数据子对象改变,拷贝的子对象也会发生变化。

(3)深拷贝,包含对象里面的子对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变,二者完全独立。

下面看一个例子:

import copy

primary_list = [1,2,3,[1,2]]
list1 = primary_list
list2 = primary_list.copy()
list3 = copy.deepcopy(primary_list)

# --直接赋值--
list1.append('a')
list1[3].append('b')

print(primary_list,'地址:',id(primary_list))    
print(list1,'地址:',id(list1))
# [1, 2, 3, [1, 2, 'b'], 'a'] 地址: 2381167401856
# [1, 2, 3, [1, 2, 'b'], 'a'] 地址: 2381167401856

# --浅拷贝--
list2.append('a')
list2[3].append('b')

print(primary_list,'地址:',id(primary_list))
print(list2,'地址:',id(list2))
# [1, 2, 3, [1, 2, 'b']] 地址: 2693332999552
# [1, 2, 3, [1, 2, 'b'], 'a'] 地址: 2693332999680


# --深拷贝--
list3.append('a')
list3[3].append('b')

print(primary_list,'地址:',id(primary_list))
print(list3,'地址:',id(list3))
# [1, 2, 3, [1, 2]] 地址: 2627768688384
# [1, 2, 3, [1, 2, 'b'], 'a'] 地址: 2627768688448

Test 22

使用 lambda 函数对 [-1, -5, 2, -3, 1, 4, -4, 3, -2, 5] 进行排序,要求排序结果正数在前(从小到大),负数在后(从大到小),如 [1, 2, 3, 4, 5, -1, -2, -3, -4, -5]。

难度:★★★☆☆

>>> lst = [-1, -5, 2, -3, 1, 4, -4, 3, -2, 5]
>>> lst.sort(key=lambda x: (x < 0, abs(x)))
# lst = sorted(lst, key=lambda x: (x < 0, abs(x)))
>>> lst
[1, 2, 3, 4, 5, -1, -2, -3, -4, -5]

Test 23

按照年龄从小到大,班级从小到大对 [{‘age’: 18, ‘class’: 2}, {‘age’: 21, ‘class’: 1}, {‘age’: 19, ‘class’: 2}, {‘age’: 19, ‘class’: 1}] 列表进行排序。

难度:★★★☆☆

>>> lst = [{'age': 18, 'class': 2}, {'age': 21, 'class': 1}, {'age': 19, 'class': 2}, {'age': 19, 'class': 1}]
>>> lst.sort(key=lambda x: (x['age'], x['class']))
>>> lst
[{'age': 18, 'class': 2}, {'age': 19, 'class': 1}, {'age': 19, 'class': 2}, {'age': 21, 'class': 1}]

Test 24

Python 中有哪些可变类型和不可变类型,它们有什么不同?

难度:★★☆☆☆

(1) 可变类型有 listdict ;不可变类型有 strinttuple

(2) 当进行修改操作时,可变类型传递的是内存中的地址,也就是说,直接修改内存中的值,并没有开辟新的内存。

(3) 不可变类型被改变时,并没有改变原内存地址中的值,而是开辟一块新的内存,将原地址中的值复制过去,对这块新开辟的内存中的值进行操作。

Test 25

写出你知道的将两个字典合并为一个新字典的方法。

难度:★★★★☆

# 示例:

dict1 = {'name': '静香', 'age': 18}

dict2 = {'name': '静香', 'sex': 'female'}

# 合并后

res = {'name': '静香', 'age': 18, 'sex': 'female'}

「1.update()更新」

dict1.update(dict2)

「2.字典推导式」

res = {k: v for dic in [dict1, dict2] for k, v in dic.items()}

「3.元素拼接」

res = dict(list(dict1.items()) + list(dict2.items()))

「4.itertools.chain」

chain() 可以将一个列表如 lists / tuples / iterables,链接在一起;返回 iterables 对象。

from itertools import chain
res = dict(chain(dict1.items(), dict2.items()))

「5.collections.ChainMap」

collections.ChainMap 可以将多个字典或映射,在逻辑上将它们合并为一个单独的映射结构

from collections import ChainMap
res = dict(ChainMap(dict2, dict1))

「6.字典拆分」

在Python3.5+中,可以下面这种字典拆分的方式:

res = {**dict1, **dict2}

Test 26

将文件内容按行读取到列表中。

难度:★★☆☆☆

with open('somefile.txt') as f:
    content = [line.strip() for line in f]    # 去掉首尾的空白字符

Test 27

Python 内置函数 any()all() 的用法。

难度:★★☆☆☆

(1)any(iterable) 函数接受一个可迭代对象 iterable 作为参数,如果 iterable「有元素为真值」则返回 True ,否则返回 False 。如果 iterable 为空则返回 False

(2)all(iterable) 函数接受一个可迭代对象 iterable 作为参数,如果 iterable「所有元素为真值」则返回 True ,否则返回 False 。如果 iterable 为空则返回 True

Test 28

谈谈 Python 中的垃圾回收机制。

难度:★★★★☆

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

「1.引用计数」

PyObject 是每个对象必有的内容,其中 ob_refcnt 就是做为引用计数。当一个对象有新的引用时,它的 ob_refcnt 就会增加,当引用它的对象被删除,它的 ob_refcnt 就会减少引用计数为 0 时,该对象生命就结束了。优点:

  1. 简单
  2. 实时性 缺点:
  3. 维护引用计数消耗资源
  4. 循环引用

「2.标记-清除机制」

基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

「3.分代技术」

分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。Python 默认定义了三代对象集合,索引数越大,对象存活时间越长。

举例:当某些内存块 M 经过了 3 次垃圾收集的清洗之后还存活时,我们就将内存块 M 划到一个集合 A 中去,而新分配的内存都划分到集合 B 中去。当垃圾收集开始工作时,大多数情况都只对集合 B 进行垃圾回收,而对集合 A 进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合 B 中的某些内存块由于存活时间长而会被转移到集合 A 中,当然,集合 A 中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

Test 29

Python 的 re 模块中 match()search() 的区别?

难度:★★★☆☆

(1)re 模块中 match(pattern, string[, flags]) ,检查 string 的开头是否与 pattern 匹配。

(2)re 模块中 research(pattern, string[, flags]),在 string 搜索 pattern 的第一个匹配值。

import re

print(re.match('super', 'superstition').span())
# (0, 5)
print(re.match('super', 'insuperable'))
# None
print(re.search('super', 'superstition').span())
# (0, 5)
print(re.search('super', 'insuperable').span())
# (2, 7)

Test 30

在读文件操作的时候会使用 read()readline() 或者 readlines(),简述它们各自的作用。

难度:★★★☆☆

read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。如果希望一行一行的输出那么就可以使用 readline(),该方法会把文件的内容加载到内存,所以对于对于大文件的读取操作来说非常的消耗内存资源,此时就可以通过 readlines 方法,将文件的句柄生成一个生产器,然后去读就可以了。

Test 31

解释一下什么是闭包?

难度:★★☆☆☆

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。

闭包中外部函数返回的不是一个具体的值,而是一个函数。一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。

#闭包函数,exponent 称为自由变量
def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of # 返回 exponent_of 函数
square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方

print(square(5))  # 计算 5 的平方
print(cube(5)) # 计算 5 的立方

25
125

Test 32

递归函数停止的条件。

难度:★★☆☆☆

递归的终止条件一般定义在递归函数内部,在递归调用前要做一个条件判断,根据判断的结果选择是继续调用自身,还是 return,返回终止递归。

终止的条件:

  • 判断递归的次数是否达到某一限定值。
  • 判断运算的结果是否达到某个范围等。

Test 33

Python 正则匹配 “good good study, day day up” 时,(g.d) 和 (g.?d) 有什么区别?

难度:★★★☆☆

  • (.*) 是贪婪匹配
  • (.*?)是懒惰匹配
>>> import re
>>> s = 'good good study, day day up!'
>>> print('贪婪模式: ', re.findall('g.*d', s))
贪婪模式:  ['good good study, day d']

>>> print('懒惰模式: ', re.findall('g.*?d', s))
懒惰模式:  ['good', 'good']

Test 34

简述解释型和编译型编程语言?

难度:★★★☆☆

编译型语言:使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。特点:在编译型语言写的程序执行之前,需要一个专门的编译过程,把源代码编译成机器语言的文件. 执行方式:「源代码 ———> 编译(一次编译) ———>目标代码———>执行(多次执行)———>输出」

解释型语言:使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。特点:解释型语言不需要事先编译,其直接将源代码解释成机器码并立即执行,所以只要某一平台提供了相应的解释器即可运行该程序。执行方式:「源代码 ———> 解释器(每次执行都需要解释)———>输出」

区别:编译型语言由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,像 C/C++、Pascal/Object Pascal(Delphi)等都是编译语言,解释型语言每次运行都需要将源代码解释成机器码并执行,效率较低,一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言,如Java、JavaScript、VBScript、Perl、Python、Ruby、MATLAB 等等。

Test 35

xrangerange 的区别?

难度:★★☆☆☆

python2 里,有两种方法获得一定范围内的数字:range() ,返回一个列表,还有 xrange() ,返回一个迭代器。python3 里,range() 返回迭代器,xrange() 不再存在。

Test 36

简述 yieldyield from 关键字。

难度:★★★★☆

当一个函数中出现 yield 关键字的时候,那么这个函数就是一个生成器 ( generator )。函数转化为 generator 后,在每次调用 next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。

yield from iterable 就是

for item in iterable:
    yield item

的语法糖。注意 yield from 后面一定是可迭代对象( iterable )

「yield的使用」

>>> def func():
 for i in range(5):
     yield i

>>> func()
<generator object func at 0x0000021812746040>
>>> f = func()
>>> next(f)
0
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
4
>>> next(f)
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    next(f)
StopIteration

「yield from 的使用」

>>> def func1():
 yield range(5)

>>> def func2():
 yield from range(5)

>>> def func3():
 for i in range(5):
     yield i

# func1()直接返回一个可迭代对象
>>> for i in func1():
 print(i)

range(0, 5)

# func2()返回迭代后的值
>>> for i in func2():
 print(i)

0
1
2
3
4

# func3()返回迭代后的值
>>> for i in func3():
 print(i)

0
1
2
3
4

Test 37

Python 中的 help()dir() 函数有什么用?

难度:★★★☆☆

help() 函数是一个内置函数,用于查看函数或模块用途的详细说明。

>>> help(max)
Help on built-in function max in module builtins:

max(...)
    max(iterable, *[, default=obj, key=func]) -> value
    max(arg1, arg2, *args, *[, key=func]) -> value

    With a single iterable argument, return its biggest item. The
    default keyword-only argument specifies an object to return if
    the provided iterable is empty.
    With two or more arguments, return the largest argument.

dir() 函数也是 Python 内置函数,dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。

>>> list1 = [1,2]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'list1']
>>> dir(list1)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Test 38

在 Python 中怎么实现线程?

难度:★★★★☆

Python 有一个多线程包 threading ,可以使用多线程来提高你代码的性能。但是 Python 有一个叫做 Global Interpreter Lock(GIL) 的构造。GIL 确保只有一个 ‘线程’ 可以在任何时候执行。

线程获取 GIL ,做一些工作,然后将 GIL 传递到下一个线程。这种情况发生得非常快,所以对于人眼而言,它可能看起来像你的线程并行执行,但它们实际上只是轮流使用相同的 CPU 内核。因此 GIL 的存在使得 Python 中的多线程无法真正的利用多核的优势来提高性能。

对于 IO 密集型操作,在等待操作系统返回的时候会释放GIL;再比如爬虫因为有等待的服务器的响应时间,可以利用多线程来加速!但是对于 CPU 密集型操作,只能通过多进程 Multiprocess 来加速。

Test 39

Python 中什么是猴子补丁?

难度:★★★★☆

猴子补丁是一种非常 Pythonic 的用法,即函数在 Python 中可以像使用变量一样对它进行赋值等操作,我们可以在运行时动态替换模块,俗称手法称为猴子补丁!我们通过对 MyClass.func 重新赋值,动态的改变了输出的结果。

>>> class MyClass:
        def func(self):
            print("enter func()")

>>> def monkey_func(self):
       print('enter monkey_func()')

>>> obj = MyClass()
>>> obj.func()
enter func()
>>> MyClass.func = monkey_func
>>> new_obj = MyClass()
>>> obj.func()
enter monkey_func()

Test 40

如何以就地操作方式打乱一个列表的元素?

难度:★★☆☆☆

使用 random 模块中导入 shuffle() 函数

>>> from random import shuffle
>>> lst = [1, 2, 3, 4, 5]
>>> shuffle(lst)
>>> lst
[4, 3, 1, 2, 5]

Test 41

将字符串 ‘love’ 更改为 ‘live’。

难度:★★☆☆☆

>>> s = 'love'

# 方法一 将str转为list
>>> list_s = list(s)
>>> list_s[1] = 'i'
>>> ''.join(list_s)
'live'

# 方法二 使用replace()
>>> s.replace('o', 'i')
'live'

# 方法三 字符串拼接
>>> s[:1] + 'i' + s[2:]
'live'

Test 42

写出下列代码的结果。

难度:★★☆☆☆

>>> def func():
 pass

>>> type(func())

上面代码运行结果为 <class 'NoneType'> ,因为是 func() ,即返回值的类型,而由于没有返回值,所以为NoneTypetype(func) 的结果为 <class 'function'>

Test 43

Python 中函数调用参数的传递方式是值传递还是引用传递?

难度:★★★☆☆

Python 的参数传递有:位置参数、默认参数、可变参数、关键字参数。

函数的传值到底是值传递还是引用传递、要分情况而定:

  • 不可变参数:是值传递,像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变不可变对象。
  • 可变参数:是引用传递,比如像列表,字典这样的对象是通过引用传递、和 C 语言里面的用指针传递数组很相似,可变对象能在函数内部改变。

Test 44

现有一个 DataFrame 数据集中包含学生的姓名与分数,先要新增一列”评价”记录学生的及格情况,如果分数低于 60 即为 “不及格”,否则 “及格”。

使用 maplambda 匿名函数,以及三元表达式来筛选满足条件的学生。

难度:★★★☆☆

>>> df = pd.DataFrame({'姓名': ['张三', '李四', '王五', '赵六'], '分数': [50, 70, 80, 90]})
>>> df
   姓名  分数
0  张三  50
1  李四  70
2  王五  80
3  赵六  90
>>> df['评价'] = df['分数'].map(lambda x: '及格' if x >= 60 else '不及格')
>>> df
   姓名  分数   评价
0  张三  50  不及格
1  李四  70   及格
2  王五  80   及格
3  赵六  90   及格

Test 45

现在为获取更详细的信息,需要将 “评价” 列进行划分,60以下不及格、60至80及格、80以上优秀。

使用 pandas.cut() 对 “评价” 列划分区间。

难度:★★★☆☆

>>> df['评价'] = pd.cut(df['分数'], [0, 60, 80, 100], labels=['不及格', '及格', '优秀'])
>>> df
   姓名  分数   评价
0  张三  50  不及格
1  李四  70   及格
2  王五  80   及格
3  赵六  90   优秀

100道Python练习题

实例001:数字组合

实例002:“个税计算”

实例003:完全平方数

实例004:这天第几天

实例005:三数排序

实例006:斐波那契数列

实例007:copy

实例008:九九乘法表

实例009:暂停一秒输出

实例010:给人看的时间

实例011:养兔子

实例012:100到200的素数

实例013:所有水仙花数

实例014:分解质因数

实例015:分数归档

实例016:输出日期

实例017:字符串构成

实例018:复读机相加

实例019:完数

实例020:高空抛物

实例021:猴子偷桃

实例022:比赛对手

实例023:画菱形

实例024:斐波那契数列II

实例025:阶乘求和

实例026:递归求阶乘

实例027:递归输出

实例028:递归求等差数列

实例029:反向输出

实例030:回文数

实例031:字母识词

实例032:反向输出II

实例033:列表转字符串

实例034:调用函数

实例035:设置输出颜色

实例036:算素数

实例037:排序

实例038:矩阵对角线之和

实例039:有序列表插入元素

实例040:逆序列表

实例041:类的方法与变量

实例042:变量作用域

实例043:作用域、类的方法与变量

实例044:矩阵相加

实例045:求和

实例046:打破循环

实例047:函数交换变量

实例048:数字比大小

实例049:lambda

实例050:随机数

实例051:按位与

实例052:按位或

实例053:按位异或

实例054:位取反、位移动

实例055:按位取反

实例056:画圈

实例057:画线

实例058:画矩形

实例059:画图

实例060:字符串长度

实例061:杨辉三角

实例062:查找字符串

实例063:画椭圆

实例064:画椭圆、矩形

实例065:画组合图形

实例066:三数排序

实例067:交换位置

实例068:旋转数列

实例069:报数

实例070:字符串长度II

实例071:输入和输出

实例072:创建链表

实例073:反向输出链表

实例074:列表排序、连接

实例075:不知所云

实例076:做函数

实例077:遍历列表

实例078:字典

实例079:字符串排序

实例080:猴子分桃

实例081:求未知数

实例082:八进制转十进制

实例083:制作奇数

实例084:连接字符串

实例085:整除

实例086:连接字符串II

实例087:访问类成员

实例088:打印星号

实例089:解码

实例090:列表详解

实例091:time模块

实例092:time模块II

实例093:time模块III

实例094:time模块IV

实例095:转换时间格式

实例096:计算复读次数

实例097:磁盘写入

实例098:磁盘写入II

实例099:磁盘读写

实例100:列表转字典

实例001:数字组合

题目: 有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少?
程序分析: 遍历全部可能,把有重复的剃掉。

total=0
for i in range(1,5):
  for j in range(1,5):
    for k in range(1,5):
      if ((i!=j)and(j!=k)and(k!=i)):
        print(i,j,k)
        total+=1print(total)

简便方法 用itertools中的permutations即可。

import itertools
sum2=0a=[1,2,3,4]
for i in itertools.permutations(a,3):
  print(i)
  sum2+=1
  print(sum2)

实例002:“个税计算”

题目: 企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润I,求应发放奖金总数?
程序分析: 分区间计算即可。

profit=int(input('Show me the money: '))
bonus=0
thresholds=[100000,100000,200000,200000,400000]
rates=[0.1,0.075,0.05,0.03,0.015,0.01]
for i in range(len(thresholds)):    
  if profit<=thresholds[i]:        
    bonus+=profit*rates[i]        
    profit=0        
    break    
  else:        
    bonus+=thresholds[i]*rates[i]        
    profit-=thresholds[i]
    bonus+=profit*rates[-1]
    print(bonus)

实例003:完全平方数

题目: 一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
程序分析: 因为168对于指数爆炸来说实在太小了,所以可以直接省略数学分析,用最朴素的方法来获取上限:

n=0
while (n+1)**2-n*n<=168:    
  n+=1
print(n+1)

思路是: 最坏的结果是n的平方与(n+1)的平方刚好差168,由于是平方的关系,不可能存在比这更大的间隙。

至于判断是否是完全平方数,最简单的方法是:平方根的值小数为0即可。

结合起来:

n=0
while (n+1)**2-n*n<=168:    
  n+=1
for i in range((n+1)**2):
   if i**0.5==int(i**0.5) and (i+168)**0.5==int((i+168)**0.5):
      print(i-100)

实例004:这天第几天

题目: 输入某年某月某日,判断这一天是这一年的第几天?
程序分析: 特殊情况,闰年时需考虑二月多加一天:

def isLeapYear(y):
  return (y%400==0 or (y%4==0 and y%100!=0))
DofM = [0,31,28,31,30,31,30,31,31,30,31,30]
res=0
year=int(input('Year:'))
month=int(input('Month:'))
day=int(input('day:'))
if isLeapYear(year):    
  DofM[2]+=1
for i in range(month):    
  res+=DofM[i]
print(res+day)

实例005:三数排序

题目: 输入三个整数x,y,z,请把这三个数由小到大输出。
程序分析: 练练手就随便找个排序算法实现一下,偷懒就直接调函数。

raw=[]
for i in range(3):    
  x=int(input('int%d: '%(i)))    
  raw.append(x)
for i in range(len(raw)):    
  for j in range(i,len(raw)):        
    if raw[i]>raw[j]:
      raw[i],raw[j]=raw[j],raw[i]
print(raw)

raw2=[]
for i in range(3):    
  x=int(input('int%d: '%(i)))    
  raw2.append(x)
print(sorted(raw2))

实例006:斐波那契数列

题目: 斐波那契数列。
程序分析: 斐波那契数列(Fibonacci sequence),从1,1开始,后面每一项等于前面两项之和。图方便就递归实现,图性能就用循环。

递归实现

def Fib(n):
  return 1 if n<=2 else Fib(n-1)+Fib(n-2)
print(Fib(int(input())))

朴素实现

target=int(input())
res=0
a,b=1,1
for i in range(target-1):    
  a,b=b,a+b
print(a)

实例007:copy

题目: 将一个列表的数据复制到另一个列表中。
程序分析: 使用列表[:],拿不准可以调用copy模块。

import copy
a = [1,2,3,4,['a','b']]

b = a          # 赋值
c = a[:]        # 浅拷贝
d = copy.copy(a)    # 浅拷贝
e = copy.deepcopy(a)  # 深拷贝

a.append(5)
a[4].append('c')

print('a=',a)
print('b=',b)
print('c=',c)
print('d=',d)
print('e=',e)
a= [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b= [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c= [1, 2, 3, 4, ['a', 'b', 'c']]
d= [1, 2, 3, 4, ['a', 'b', 'c']]
e= [1, 2, 3, 4, ['a', 'b']]

实例008:九九乘法表

题目: 输出 99 乘法口诀表。
*
程序分析:** 分行与列考虑,共9行9列,i控制行,j控制列。

for i in range(1,10):
    for j in range(1,i+1):
        print('%d*%d=%2ld '%(i,j,i*j),end='')
    print()

实例009:暂停一秒输出

题目: 暂停一秒输出。
程序分析: 使用 time 模块的 sleep() 函数。

import time
for i in range(4):
print(str(int(time.time()))[-2:])
    time.sleep(1)

实例010:给人看的时间

题目: 暂停一秒输出,并格式化当前时间。
程序分析: 使用 time 模块的 sleep() 函数。

import time
for i in range(4):    
  print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
  time.sleep(1)

实例011:养兔子

题目: 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

程序分析: 我认为原文的解法有点扯,没有考虑3个月成熟的问题,人家还是婴儿怎么生孩子?考虑到三个月成熟,可以构建四个数据,其中:一月兔每个月长大成为二月兔,二月兔变三月兔,三月兔变成年兔,成年兔(包括新成熟的三月兔)生等量的一月兔。

month=int(input('繁殖几个月?:'))
month_1=1
month_2=0
month_3=0
month_elder=0
for i in range(month):
    month_1,month_2,month_3,month_elder=month_elder+month_3,month_1,month_2,month_elder+month_3
    print('第%d个月共'%(i+1),month_1+month_2+month_3+month_elder,'对兔子')
    print('其中1月兔:',month_1)
    print('其中2月兔:',month_2)
    print('其中3月兔:',month_3)
    print('其中成年兔:',month_elder)

实例012:100到200的素数

题目: 判断101-200之间有多少个素数,并输出所有素数。
程序分析: 判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。用else可以进一步简化代码.

import math
for i in range(100,200):
    flag=0
    for j in range(2,round(math.sqrt(i))+1):
        if i%j==0:
            flag=1
            break
    if flag:
        continue
    print(i)

print('\nSimplify the code with "else"\n')

for i in range(100,200):
    for j in range(2,round(math.sqrt(i))+1):
        if i%j==0:
            break
    else:
        print(i)

实例013:所有水仙花数

题目: 打印出所有的”水仙花数”,所谓”水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个”水仙花数”,因为153=1的三次方+5的三次方+3的三次方。

程序分析: 利用for循环控制100-999个数,每个数分解出个位,十位,百位。

for i in range(100,1000):
    s=str(i)
    one=int(s[-1])
    ten=int(s[-2])
    hun=int(s[-3])
    if i == one**3+ten**3+hun**3:
        print(i)

实例014:分解质因数

题目: 将一个整数分解质因数。例如:输入90,打印出90=2335。
*
程序分析:** 根本不需要判断是否是质数,从2开始向数本身遍历,能整除的肯定是最小的质数。

target=int(input('输入一个整数:'))
print(target,'= ',end='')

if target<0:
    target=abs(target)
print('-1*',end='')

flag=0
if target<=1:
print(target)
    flag=1

while True:
if flag:
break
for i in range(2,int(target+1)):
if target%i==0:
print("%d"%i,end='')
if target==i:
                flag=1
break
print('*',end='')
            target/=i
break

实例015:分数归档

题目: 利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。
程序分析: 用条件判断即可。

points=int(input('输入分数:'))
if points>=90:
    grade='A'
elif points<60:
    grade='C'
else:
   grade='B
print(grade)

实例016:输出日期

题目: 输出指定格式的日期。
程序分析: 使用 datetime 模块。

import datetime
print(datetime.date.today())
print(datetime.date(2333,2,3))
print(datetime.date.today().strftime('%d/%m/%Y'))
day=datetime.date(1111,2,3)
day=day.replace(year=day.year+22)
print(day)

实例017:字符串构成

题目: 输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。
程序分析: 利用 while 或 for 语句,条件为输入的字符不为 ‘\n’。

string=input("输入字符串:")
alp=0
num=0
spa=0
oth=0
for i in range(len(string)):
if string[i].isspace():
        spa+=1
    elif string[i].isdigit():
        num+=1
    elif string[i].isalpha():
        alp+=1
else:
        oth+=1
print('space: ',spa)
print('digit: ',num)
print('alpha: ',alp)
print('other: ',oth)

实例018:复读机相加

题目: 求s=a+aa+aaa+aaaa+aa…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制。
程序分析: 用字符串解决。

a=input('被加数字:')
n=int(input('加几次?:'))
res=0
for i in range(n):
    res+=int(a)
    a+=a[0]
print('结果是:',res)

实例019:完数

题目: 一个数如果恰好等于它的因子之和,这个数就称为”完数”。例如6=1+2+3.编程找出1000以内的所有完数。
程序分析: 将每一对因子加进集合,在这个过程中已经自动去重。最后的结果要求不计算其本身。

def factor(num):
    target=int(num)
    res=set()
for i in range(1,num):
if num%i==0:
            res.add(i)
            res.add(num/i)
return res

for i in range(2,1001):
if i==sum(factor(i))-i:
        print(i)

实例020:高空抛物

题目: 一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?

high=200.
total=100
for i in range(10):
    high/=2
    total+=high
    print(high/2)
print('总长:',total)

实例021:猴子偷桃

题目: 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。

程序分析: 按规则反向推断:猴子有一个桃子,他偷来一个桃子,觉得不够又偷来了与手上等量的桃子,一共偷了9天。

peach=1
for i in range(9):
    peach=(peach+1)*2
print(peach)

实例022:比赛对手

题目: 两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。

程序分析: 找到条件下不重复的三个对手即可。

a=set(['x','y','z'])
b=set(['x','y','z'])
c=set(['x','y','z'])
c-=set(('x','y'))
a-=set('x')
for i in a:
    for j in b:
        for k in c:
            if len(set((i,j,k)))==3:
                print('a:%s,b:%s,c:%s'%(i,j,k))

实例023:画菱形

题目: 打印出如下图案(菱形):

    *
   ***
  *****
 *******
  *****
   ***
    *

程序分析: 递归调用即可。

def draw(num):
    a="*"*(2*(4-num)+1)
    print(a.center(9,' '))
if num!=1:
        draw(num-1)
        print(a.center(9,' '))
draw(4)

实例024:斐波那契数列II

题目: 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13…求出这个数列的前20项之和。
程序分析: 就是斐波那契数列的后一项除以前一项。

a = 2.0
b = 1.0
s = 0
for n in range(1,21):
    s += a / b
    a,b = a + b,a
print (s)

实例025:阶乘求和

题目: 求1+2!+3!+…+20!的和。
程序分析: 1+2!+3!+…+20!=1+2(1+3(1+4(…20(1))))

res=1
for i in range(20,1,-1):
    res=i*res+1
print(res)

实例026:递归求阶乘

题目: 利用递归方法求5!。
程序分析: 递归调用即可。

def factorial(n):
    return n*factorial(n-1) if n>1 else 1
print(factorial(5))

实例027:递归输出

题目: 利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。
程序分析: 递归真是蠢方法。

def rec(string):
if len(string)!=1:
    rec(string[1:])
print(string[0],end='')
rec(input('string here:'))

实例028:递归求等差数列

题目: 有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?
程序分析: 就一等差数列。

def age(n):
   if n==1:
   return 10
  return 2+age(n-1)
print(age(5))

实例029:反向输出

题目: 给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。
程序分析: 学会分解出每一位数,用字符串的方法总是比较省事。

n=int(input('输入一个正整数:'))
n=str(n)
print('%d位数'%len(n))
print(n[::-1])

实例030:回文数

题目: 一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。
程序分析: 用字符串比较方便,就算输入的不是数字都ok。

n=input("随便你输入啥啦:")
a=0
b=len(n)-1
flag=True
while a<b:
if n[a]!=n[b]:
print('不是回文串')
        flag=False
break
    a,b=a+1,b-1
if flag:
print('是回文串')

实例031:字母识词

题目: 请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。
程序分析: 这里用字典的形式直接将对照关系存好。

weekT={'h':'thursday','u':'tuesday'}
weekS={'a':'saturday','u':'sunday'}
week={'t':weekT,
   's':weekS,
   'm':'monday',
   'w':'wensday',
   'f':'friday'}
a=week[str(input('请输入第一位字母:')).lower()]
if a==weekT or a==weekS:
  print(a[str(input('请输入第二位字母:')).lower()])
else:
    print(a)

实例032:反向输出II

题目: 按相反的顺序输出列表的值。

a = ['one', 'two', 'three']
print(a[::-1])

实例033:列表转字符串

题目: 按逗号分隔列表。

L = [1,2,3,4,5]
print(','.join(str(n) for n in L))

实例034:调用函数

题目: 练习函数调用。

def hello():
    print('Hello World!')
def helloAgain():
for i in range(2):
        hello()
if __name__=='__main__':
    helloAgain()

实例035:设置输出颜色

题目: 文本颜色设置。

class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
print(bcolors.WARNING + "警告的颜色字体?" + bcolors.ENDC)

实例036:算素数

题目: 求100之内的素数。
程序分析: 用else执行for循环的奖励代码(如果for是正常完结,非break)。

lo=int(input('下限:'))
hi=int(input('上限:'))
for i in range(lo,hi+1):
    if i > 1:
        for j in range(2,i):
            if (i % j) == 0:
                break
        else:
            print(i)

实例037:排序

题目: 对10个数进行排序。
程序分析: 同实例005。

raw=[]
for i in range(10):
    x=int(input('int%d: '%(i)))
    raw.append(x)

for i in range(len(raw)):
for j in range(i,len(raw)):
if raw[i]>raw[j]:
            raw[i],raw[j]=raw[j],raw[i]
print(raw)

实例038:矩阵对角线之和

题目: 求一个3*3矩阵主对角线元素之和。

mat=[[1,2,3],
     [3,4,5],
     [4,5,6]
    ]
res=0
for i in range(len(mat)):
    res+=mat[i][i]
print(res)

实例039:有序列表插入元素

题目: 有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
程序分析: 首先判断此数是否大于最后一个数,然后再考虑插入中间的数的情况,插入后此元素之后的数,依次后移一个位置。

lis=[1,10,100,1000,10000,100000]
n=int(input('insert a number: '))
lis.append(n)
for i in range(len(lis)-1):
    if lis[i]>=n:
        for j in range(i,len(lis)):
            lis[j],lis[-1]=lis[-1],lis[j]
        break
print(lis)

实例040:逆序列表

题目: 将一个数组逆序输出。
程序分析: 依次交换位置,或者直接调用reverse方法。

lis=[1,10,100,1000,10000,100000]
for i in range(int(len(lis)/2)):
    lis[i],lis[len(lis)-1-i]=lis[len(lis)-1-i],lis[i]
print('第一种实现:')
print(lis)

lis=[1,10,100,1000,10000,100000]
print('第二种实现:')
lis.reverse()
print(lis)

实例041:类的方法与变量

题目: 模仿静态变量的用法。
程序分析: 构造类,了解类的方法与变量。

def dummy():
    i=0
    print(i)
    i+=1

class cls:
    i=0
    def dummy(self):
        print(self.i)
        self.i+=1

a=cls()
for i in range(50):
    dummy()
    a.dummy()

实例042:变量作用域

题目: 学习使用auto定义变量的用法。
程序分析: python中的变量作用域。

i=0
n=0
def dummy():
    i=0
    print(i)
    i+=1
def dummy2():
global n
    print(n)
    n+=1
print('函数内部的同名变量')
for j in range(20):
    print(i)
    dummy()
    i+=1
print('global声明同名变量')
for k in range(20):
    print(n)
    dummy2()
    n+=10

实例043:作用域、类的方法与变量

题目: 模仿静态变量(static)另一案例。
程序分析: 综合实例041和实例042。

class dummy:
    num=1
    def Num(self):
        print('class dummy num:',self.num)
        print('global num: ',num)
        self.num+=1

n=dummy()
num=1
for i in range(5):
    num*=10
    n.Num()

实例044:矩阵相加

题目: 计算两个矩阵相加。
程序分析: 创建一个新的矩阵,使用 for 迭代并取出 X 和 Y 矩阵中对应位置的值,相加后放到新矩阵的对应位置中。

X = [[12,7,3],
    [4 ,5,6],
    [7 ,8,9]]

Y = [[5,8,1],
    [6,7,3],
    [4,5,9]]

res=[[0,0,0],
    [0,0,0],
    [0,0,0]]
for i in range(len(res)):
    for j in range(len(res[0])):
        res[i][j]=X[i][j]+Y[i][j]
print(res)

实例045:求和

题目: 统计 1 到 100 之和。

res=0
for i in range(1,101):
    res+=i
print(res)

实例046:打破循环

题目: 求输入数字的平方,如果平方运算后小于 50 则退出。

while True:
    try:
        n=float(input('输入一个数字:'))
    except:
        print('输入错误')
        continue
    dn=n**2
    print('其平方为:',dn)
    if dn<50:
        print('平方小于50,退出')
        break

实例047:函数交换变量

题目: 两个变量值用函数互换。

def exc(a,b):
    return (b,a)
a=0
b=10
a,b=exc(a,b)
print(a,b)

实例048:数字比大小

题目: 数字比较。

a=int(input('a='))
b=int(input('b='))
if a<b:
    print('a<b')
elif a>b:
    print('a>b')
else:
    print('a=b')

实例049:lambda

题目: 使用lambda来创建匿名函数。

Max=lambda x,y:x*(x>=y)+y*(y>x)
Min=lambda x,y:x*(x<=y)+y*(y<x)

a=int(input('1:'))
b=int(input('2:'))

print(Max(a,b))
print(Min(a,b))

实例050:随机数

题目: 输出一个随机数。
程序分析: 使用 random 模块。

import random
print(random.uniform(10,20))

实例051:按位与

题目: 学习使用按位与 & 。
程序分析: 0&0=0; 0&1=0; 1&0=0; 1&1=1。

a=0o77
print(a)
b=a&3
print(b)
b=b&7
print(b)

实例052:按位或

题目: 学习使用按位或 | 。
程序分析: 0|0=0; 0|1=1; 1|0=1; 1|1=1

a=0o77
print(a|3)
print(a|3|7)

实例053:按位异或

题目: 学习使用按位异或 ^ 。
程序分析: 0^0=0; 0^1=1; 1^0=1; 1^1=0

a=0o77
print(a^3)
print(a^3^7)

实例054:位取反、位移动

题目: 取一个整数a从右端开始的4~7位。
程序分析: 可以这样考虑:

  1. 先使a右移4位。
  2. 设置一个低4位全为1,其余全为0的数。可用(0<<4)
  3. 将上面二者进行&运算。
a=int(input('输入一个数字: '))
b=0                 #     0
b=~b                #     1
b=b<<4              # 10000
b=~b                #  1111
c=a>>4
d=c&b
print('a:',bin(a))
print('b:',bin(b))
print('c:',bin(c))
print('d:',bin(d))

实例055:按位取反

题目: 学习使用按位取反~。
程序分析: ~0=1; ~1=0;

print(~234)
print(~~234)

实例056:画圈

题目: 画图,学用circle画圆形。

from tkinter import *
canvas=Canvas(width=800,height=600,bg='yellow')
canvas.pack(expand=YES,fill=BOTH)
k=1
j=1
for i in range(26):
    canvas.create_oval(310-k,250-k,310+k,250+k,width=1)
    k+=j
    j+=0.3
mainloop()

实例057:画线

题目: 画图,学用line画直线。

from tkinter import *

canvas = Canvas(width=300, height=300, bg='green')   
canvas.pack(expand=YES, fill=BOTH)                  
x0 = 263
y0 = 263
y1 = 275
x1 = 275
for i in range(19):
    canvas.create_line(x0,y0,x0,y1, width=1, fill='red')
    x0 = x0 - 5
    y0 = y0 - 5
    x1 = x1 + 5
    y1 = y1 + 5

x0 = 263
y1 = 275
y0 = 263
for i in range(21):
    canvas.create_line(x0,y0,x0,y1,fill = 'red')
    x0 += 5
    y0 += 5
    y1 += 5

实例058:画矩形

题目: 画图,学用rectangle画方形。

from tkinter import *
root = Tk()
root.title('Canvas')
canvas = Canvas(root,width = 400,height = 400,bg = 'yellow')
x0 = 263
y0 = 263
y1 = 275
x1 = 275
for i in range(19):
    canvas.create_rectangle(x0,y0,x1,y1)
    x0 -= 5
    y0 -= 5
    x1 += 5
    y1 += 5
canvas.pack()
root.mainloop()

实例059:画图

题目: 画图,综合例子。

from tkinter import *
canvas = Canvas(width = 300,height = 300,bg = 'green')
canvas.pack(expand = YES,fill = BOTH)
x0 = 150
y0 = 100
canvas.create_oval(x0 - 10,y0 - 10,x0 + 10,y0 + 10)
canvas.create_oval(x0 - 20,y0 - 20,x0 + 20,y0 + 20)
canvas.create_oval(x0 - 50,y0 - 50,x0 + 50,y0 + 50)
import math
B = 0.809
for i in range(16):
    a = 2 * math.pi / 16 * i
    x = math.ceil(x0 + 48 * math.cos(a))
    y = math.ceil(y0 + 48 * math.sin(a) * B)
    canvas.create_line(x0,y0,x,y,fill = 'red')
canvas.create_oval(x0 - 60,y0 - 60,x0 + 60,y0 + 60)

for k in range(501):
    for i in range(17):
        a = (2 * math.pi / 16) * i + (2 * math.pi / 180) * k
        x = math.ceil(x0 + 48 * math.cos(a))
        y = math.ceil(y0 + 48 + math.sin(a) * B)
        canvas.create_line(x0,y0,x,y,fill = 'red')
    for j in range(51):
        a = (2 * math.pi / 16) * i + (2* math.pi / 180) * k - 1
        x = math.ceil(x0 + 48 * math.cos(a))
        y = math.ceil(y0 + 48 * math.sin(a) * B)
        canvas.create_line(x0,y0,x,y,fill = 'red')
mainloop()

实例060:字符串长度

题目: 计算字符串长度。

s='zhangguang101'
print(len(s))

实例061:杨辉三角

题目: 打印出杨辉三角形前十行。

def generate(numRows):
    r = [[1]]
    for i in range(1,numRows):
        r.append(list(map(lambda x,y:x+y, [0]+r[-1],r[-1]+[0])))
    return r[:numRows]
a=generate(10)
for i in a:
    print(i)

实例062:查找字符串

题目: 查找字符串。

s1='aabbxuebixuebi'
s2='ab'
s3='xue'
print(s1.find(s2))
print(s1.find(s3))

实例063:画椭圆

题目: 画椭圆。
程序分析: 使用 tkinter。

from tkinter import *
x = 360
y = 160
top = y - 30
bottom = y - 30

canvas = Canvas(width = 400,height = 600,bg = 'white')
for i in range(20):
    canvas.create_oval(250 - top,250 - bottom,250 + top,250 + bottom)
    top -= 5
    bottom += 5
canvas.pack()
mainloop()

实例064:画椭圆、矩形

题目: 利用ellipse 和 rectangle 画图。。

from tkinter import *    
canvas = Canvas(width = 400,height = 600,bg = 'white')    
left = 20    
right = 50    
top = 50    
num = 15    
for i in range(num):        
    canvas.create_oval(250 - right,250 - left,250 + right,250 + left)        
    canvas.create_oval(250 - 20,250 - top,250 + 20,250 + top)        
    canvas.create_rectangle(20 - 2 * i,20 - 2 * i,10 * (i + 2),10 * ( i + 2))        
    right += 5        
    left += 5        
    top += 10    
canvas.pack()    
mainloop()

实例065:画组合图形

题目: 一个最优美的图案。

import math
from tkinter import *

class PTS:
    def __init__(self):
        self.x = 0
        self.y = 0
points = []

def LineToDemo():
    screenx = 400
    screeny = 400
    canvas = Canvas(width = screenx,height = screeny,bg = 'white')

    AspectRatio = 0.85
    MAXPTS = 15
    h = screeny
    w = screenx
    xcenter = w / 2
    ycenter = h / 2
    radius = (h - 30) / (AspectRatio * 2) - 20
    step = 360 / MAXPTS
    angle = 0.0
    for i in range(MAXPTS):
        rads = angle * math.pi / 180.0
        p = PTS()
        p.x = xcenter + int(math.cos(rads) * radius)
        p.y = ycenter - int(math.sin(rads) * radius * AspectRatio)
        angle += step
        points.append(p)
    canvas.create_oval(xcenter - radius,ycenter - radius,
                       xcenter + radius,ycenter + radius)
    for i in range(MAXPTS):
        for j in range(i,MAXPTS):
            canvas.create_line(points[i].x,points[i].y,points[j].x,points[j].y)

    canvas.pack()
    mainloop()
if __name__ == '__main__':
    LineToDemo()

实例066:三数排序

题目: 输入3个数a,b,c,按大小顺序输出。

raw=[]
for i in range(3):
    x=int(input('int%d: '%(i)))
    raw.append(x)

for i in range(len(raw)):
for j in range(i,len(raw)):
if raw[i]>raw[j]:
            raw[i],raw[j]=raw[j],raw[i]
print(raw)

raw2=[]
for i in range(3):
    x=int(input('int%d: '%(i)))
    raw2.append(x)
print(sorted(raw2))

实例067:交换位置

题目: 输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。

li=[3,2,5,7,8,1,5]

li[-1],li[li.index(min(li))]=li[li.index(min(li))],li[-1]

m=li[0]
ind=li.index(max(li))
li[0]=li[ind]
li[ind]=m

print(li)

实例068:旋转数列

题目: 有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

from collections import *
li=[1,2,3,4,5,6,7,8,9]
deq=deque(li,maxlen=len(li))
print(li)
deq.rotate(int(input('rotate:')))
print(list(deq))

实例069:报数

题目: 有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。

nmax = 50
n = int(input('请输入总人数:'))
num = []
for i in range(n):
    num.append(i + 1)

i = 0
k = 0
m = 0

while m < n - 1:
    if num[i] != 0 : k += 1
    if k == 3:
        num[i] = 0
        k = 0
        m += 1
    i += 1
    if i == n : i = 0

i = 0
while num[i] == 0: i += 1
print(num[i])

实例070:字符串长度II

题目: 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。

def lenofstr(s):    
   return len(s)
print(lenofstr('tanxiaofengsheng'))

实例071:输入和输出

题目: 编写input()和output()函数输入,输出5个学生的数据记录。

N = 3
#stu
#num : string
#name : string
liststudent = []
for i in range(5):    
   student.append(['','',[]])
def input_stu(stu):    
   for i in range(N):        
       stu[i][0] = input('input student num:\n')        
        stu[i][1] = input('input student name:\n')        
    for j in range(3):            
       stu[i][2].append(int(input('score:\n')))
def output_stu(stu):    
   for i in range(N):        
       print ('%-6s%-10s' % ( stu[i][0],stu[i][1] ))        
    for j in range(3):            
       print ('%-8d' % stu[i][2][j])
if __name__ == '__main__':    
   input_stu(student)    
   print (student)    
   output_stu(student)

实例072:创建链表

题目: 创建一个链表。

class Node:

    def __init__(self, data):
        self.data = data
        self.next = None

    def get_data(self):
        return self.data

class List:

    def __init__(self, head):
        self.head = head

    def is_empty(self): 
        return self.get_len() == 0

    def get_len(self):  
        length = 0
        temp = self.head
        while temp is not None:
            length += 1
            temp = temp.next
        return length

    def append(self, node):
        temp = self.head
        while temp.next is not None:
            temp = temp.next
        temp.next = node

    def delete(self, index): 
        if index < 1 or index > self.get_len():
            print("给定位置不合理")
            return
        if index == 1:
            self.head = self.head.next
            return
        temp = self.head
        cur_pos = 0
        while temp is not None:
            cur_pos += 1
            if cur_pos == index-1:
                temp.next = temp.next.next
            temp = temp.next

    def insert(self, pos, node):
        if pos < 1 or pos > self.get_len():
            print("插入结点位置不合理")
            return
        temp = self.head
        cur_pos = 0
        while temp is not Node:
            cur_pos += 1
            if cur_pos == pos-1:
                node.next = temp.next
                temp.next =node
                break
            temp = temp.next

    def reverse(self, head):
        if head is None and head.next is None:
            return head
        pre = head
        cur = head.next
        while cur is not None:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        head.next = None
        return pre

    def print_list(self, head):
        init_data = []
        while head is not None:
            init_data.append(head.get_data())
            head = head.next
        return init_data

if __name__=='__main__':
    head=Node('head')
    link=List(head)
    for i in range(10):
        node=Node(i)
        link.append(node)
    print(link.print_list(head))

实例073:反向输出链表

题目: 反向输出一个链表。

class Node:

    def __init__(self, data):
        self.data = data
        self.next = None

    def get_data(self):
        return self.data

class List:

    def __init__(self, head):
        self.head = head

    def is_empty(self): 
        return self.get_len() == 0

    def get_len(self):  
        length = 0
        temp = self.head
        while temp is not None:
            length += 1
            temp = temp.next
        return length

    def append(self, node):
        temp = self.head
        while temp.next is not None:
            temp = temp.next
        temp.next = node

    def delete(self, index): 
        if index < 1 or index > self.get_len():
            print("给定位置不合理")
            return
        if index == 1:
            self.head = self.head.next
            return
        temp = self.head
        cur_pos = 0
        while temp is not None:
            cur_pos += 1
            if cur_pos == index-1:
                temp.next = temp.next.next
            temp = temp.next

    def insert(self, pos, node):
        if pos < 1 or pos > self.get_len():
            print("插入结点位置不合理")
            return
        temp = self.head
        cur_pos = 0
        while temp is not Node:
            cur_pos += 1
            if cur_pos == pos-1:
                node.next = temp.next
                temp.next =node
                break
            temp = temp.next

    def reverse(self, head):
        if head is None and head.next is None:
            return head
        pre = head
        cur = head.next
        while cur is not None:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        head.next = None
        return pre

    def print_list(self, head):
        init_data = []
        while head is not None:
            init_data.append(head.get_data())
            head = head.next
        return init_data

if __name__=='__main__':
    head=Node('head')
    link=List(head)
    for i in range(10):
        node=Node(i)
        link.append(node)
    print(link.print_list(head))
    print(link.print_list(link.reverse(head)))

实例074:列表排序、连接

题目: 列表排序及连接。
程序分析: 排序可使用 sort() 方法,连接可以使用 + 号或 extend() 方法。

a=[2,6,8]
b=[7,0,4]
a.extend(b)
a.sort()
print(a)

实例075:不知所云

题目: 放松一下,算一道简单的题目。
程序分析: 鬼知道是什么。

for i in range(5):
    n = 0
    if i != 1: 
       n += 1
    if i == 3: 
       n += 1
    if i == 4: 
       n += 1
    if i != 4: 
       n += 1
    if n == 3: 
      print (64 + i)

实例076:做函数

题目: 编写一个函数,输入n为偶数时,调用函数求1/2+1/4+…+1/n,当输入n为奇数时,调用函数1/1+1/3+…+1/n

def peven(n):
    i = 0
    s = 0.0
    for i in range(2,n + 1,2):
        s += 1.0 / i
    return s

def podd(n):
    s = 0.0
    for i in range(1, n + 1,2):
        s += 1.0 / i
    return s

def dcall(fp,n):
    s = fp(n)
    return s

if __name__ == '__main__':
    n = int(input('input a number: '))
    if n % 2 == 0:
        sum = dcall(peven,n)
    else:
        sum = dcall(podd,n)
    print (sum)

实例077:遍历列表

题目: 循环输出列表

l=['moyu','niupi','xuecaibichi','shengfaji','42']
for i in range(len(l)):    
   print(l[i])

实例078:字典

题目: 找到年龄最大的人,并输出。请找出程序中有什么问题。

person = {"li":18,"wang":50,"zhang":20,"sun":22}
m = 'li'
for key in person.keys():
    if person[m] < person[key]:
        m = key

print ('%s,%d' % (m,person[m]))

实例079:字符串排序

题目: 字符串排序。

l=['baaa','aaab','aaba','aaaa','abaa']
l.sort()
print(l)

实例080:猴子分桃

题目: 海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子平均分为五份,多了一个,这只猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一个,它同样把多的一个扔入海中,拿走了一份,第三、第四、第五只猴子都是这样做的,问海滩上原来最少有多少个桃子?

i = 0
j = 1
x = 0
while (i < 5) :
    x = 4 * j
    for i in range(0,5) :
        if(x%4 != 0) :
            break
        else :
            i += 1
        x = (x/4) * 5 +1
    j += 1
print(x)

for p in range(5):
    x=(x-1)/5*4
print(x)

实例081:求未知数

题目: 809??=800??+9?? 其中??代表的两位数, 809??为四位数,8??的结果为两位数,9??的结果为3位数。求??代表的两位数,及809*??后的结果。

a = 809
for i in range(10,100):
    b = i * a
if b >= 1000 and b <= 10000 and 8 * i < 100 and 9 * i >= 100:
        print(b,' = 800 * ', i, ' + 9 * ', i)

for i in range(10,100):
if 8*i>99 or 9*i<100:
continue
if 809*i==800*i+9*i:
        print(i)
break

实例082:八进制转十进制

题目: 八进制转换为十进制

n=eval('0o'+str(int(input('八进制输入:'))))
print(n)

实例083:制作奇数

题目: 求0—7所能组成的奇数个数。
程序分析:

  • 组成1位数是4个。1,3,5,7结尾
  • 组成2位数是7*4个。第一位不能为0
  • 组成3位数是784个。中间随意
  • 组成4位数是788*4个。
sum = 4
s = 4
for j in range(2,9):
   print(sum)
    if j <= 2:
       s *= 7
    else:
       s *= 8
    sum += s   
  print('sum = %d' % sum)

实例084:连接字符串

题目: 连接字符串。

delimiter = ','
mylist = ['Brazil', 'Russia', 'India', 'China']
print(delimiter.join(mylist))

实例085:整除

题目: 输入一个奇数,然后判断最少几个 9 除于该数的结果为整数。
程序分析: 999999 / 13 = 76923。

zi = int(input('输入一个数字:'))
n1 = 1
c9 = 1
m9 = 9
sum = 9
while n1 != 0:
    if sum % zi == 0:
        n1 = 0
    else:
        m9 *= 10
        sum += m9
        c9 += 1
print ('%d 个 9 可以被 %d 整除 : %d' % (c9,zi,sum))
r = sum / zi
print ('%d / %d = %d' % (sum,zi,r))

实例086:连接字符串II

题目: 两个字符串连接程序。

a='guangtou'
b='feipang'
print(b+a)

实例087:访问类成员

题目: 结构体变量传递。

class student:        
    x = 0        
    c = 0    
    def f(stu):        
        stu.x = 20        
        stu.c = 'c'    
        a= student()    
        a.x = 3    
        a.c = 'a'   
        f(a)    
print(a.x,a.c)

实例088:打印星号

题目: 读取7个数(1—50)的整数值,每读取一个值,程序打印出该值个数的*。

for i in range(3):
   print('*'*int(input('input a number: ')))

实例089:解码

题目: 某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。

n=input()
n = str(n)
a=[]
for i in range(4):
    a.append(int(n[i])+5)
a[0],a[3]=a[3],a[0]
a[1],a[2]=a[2],a[1]
print ("".join('%s' %s for s in a))

实例090:列表详解

题目: 列表使用实例。

#list  
#新建列表 
testList=[10086,'中国移动',[1,2,4,5]]  

#访问列表长度  
print (len(testList)  )
#到列表结尾
 print (testList[1:])
#向列表添加元素    
testList.append('i\'m new here!')  

print (len(testList)  )
print (testList[-1]  )
#弹出列表的最后一个元素  
print (testList.pop(1)  )
print (len(testList)  )
print (testList  )
st comprehension  
#后面有介绍,暂时掠过  
matrix = [[1, 2, 3],  
[4, 5, 6],  
[7, 8, 9]]  
print (matrix  )
print (matrix[1]  )
col2 = [row[1] for row in matrix]#get a  column from a matrix  
print (col2  )
col2even = [row[1] for row in matrix if  row[1] % 2 == 0]#filter odd item  
print (col2even)

实例091:time模块

题目: 时间函数举例1。

import time    
print(time.ctime(time.time()))    
print(time.asctime(time.localtime(time.time())))    
print(time.asctime(time.gmtime(time.time())))

实例092:time模块II

题目: 时间函数举例2。

程序分析: 如何浪费时间。

import time    
start = time.time()    
for i in range(3000):        
   print(i)    
end = time.time()    
print (end - start)

实例093:time模块III

题目: 时间函数举例3。
程序分析: 如何浪费时间。

import time    
start = time.clock()    
for i in range(100):        
   print(i)    
end = time.clock()    
print('different is %6.3f' % (end - start))

实例094:time模块IV

题目: 时间函数举例4。

import time
import random

play_it = input('do you want to play it.(\'y\' or \'n\')')
while play_it == 'y':
    c = input('input a character:\n')
    i = random.randint(0,2**32) % 100
    print ('please input number you guess:\n')
    start = time.clock()
    a = time.time()
    guess = int(input('input your guess:\n'))
    while guess != i:
        if guess > i:
            print('please input a little smaller')
            guess = int(input('input your guess:\n'))
        else:
            print('please input a little bigger')
            guess = int(input('input your guess:\n'))
    end = time.clock()
    b = time.time()
    var = (end - start) / 18.2
    print (var)
    # print 'It took you %6.3 seconds' % time.difftime(b,a))
    if var < 15:
        print ('you are very clever!')
    elif var < 25:
        print ('you are normal!')
    else:
        print ('you are stupid!')
    print ('Congradulations')
    print ('The number you guess is %d' % i)
    play_it = input('do you want to play it.')

实例095:转换时间格式

题目: 字符串日期转换为易读的日期格式。
程序分析: dateutil是个第三方库。

from dateutil import parser
dt = parser.parse("Aug 28 2015 12:00AM")
print (dt)

实例096:计算复读次数

题目: 计算字符串中子串出现的次数。

s1='xuebixuebixuebixuebixuebixuebixuebixue'
s2='xuebi'
print(s1.count(s2))

实例097:磁盘写入

题目: 从键盘输入一些字符,逐个把它们写到磁盘文件上,直到输入一个 # 为止。

from sys import stdout
filename = input('输入文件名:\n')
fp = open(filename,"w")
ch = input('输入字符串:\n')
while ch != '#':
    fp.write(ch)
    stdout.write(ch)
    ch = input('')
fp.close()

实例098:磁盘写入II

题目: 从键盘输入一个字符串,将小写字母全部转换成大写字母,然后输出到一个磁盘文件”test”中保存。

fp = open('test.txt','w')
string = input('please input a string:\n')
string = string.upper()
fp.write(string)
fp = open('test.txt','r')
print (fp.read())
fp.close()

实例099:磁盘读写

题目: 有两个磁盘文件A和B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列), 输出到一个新文件C中。

import string
fp = open('test1.txt')
a = fp.read()
fp.close()

fp = open('test2.txt')
b = fp.read()
fp.close()

fp = open('test3.txt','w')
l = list(a + b)
l.sort()
s = ''
s = s.join(l)
fp.write(s)
fp.close()

实例100:列表转字典

题目: 列表转换为字典。

i = ['a', 'b']
l = [1, 2]
print (dict(zip(i,l)))

文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录