6578 字
33 分钟
页面浏览--访问次数--
公开课 教程全文(4 篇合并)

本篇目录#

目录里每一条都可以点开直达对应章节;右侧(移动端为悬浮按钮)的「文章目录」也会列出同一批标题。


01 好玩的Python#

因为下面的代码都非常简单,简单到直接使用Python的交互式环境就能完成。当然,官方Python自带的交互式环境比较难用,推荐大家使用ipython,可以使用下面的命令来安装ipython,安装成功后键入ipython命令就能进入交互式环境。

Terminal window
pip install ipython

或

Terminal window
pip3 install ipython

ipython最直观的优点:

  1. 可以用?或者??来获取帮助。
  2. 可以用!调用系统命令。
  3. 可以使用Tab键自动补全。
  4. 可以使用魔法指令,如:%timeit。

没有工具用代码也能P图#

  1. 安装pillow三方库。

    PIL(Python Imaging Library)是Python平台事实上的图像处理标准库了。PIL功能非常强大,而API却非常简单易用。但是PIL仅支持到Python 2.7,而且很多年都没有人维护了,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,除了支持Python 3.x还加入了很多有用且有趣的新特性。

    Terminal window
    pip install pillow

    或

    Terminal window
    pip3 install pillow
  2. 加载图片。

    from PIL import Image
    chiling = Image.open('chiling.jpg')
    chiling.show()
  3. 使用滤镜。

    Terminal window
    from PIL import ImageFilter
    chiling.filter(ImageFilter.EMBOSS).show()
    chiling.filter(ImageFilter.CONTOUR).show()
  4. 图像剪裁和粘贴。

    rect = 220, 690, 265, 740
    watch = chiling.crop(rect)
    watch.show()
    blured_watch = watch.filter(ImageFilter.GaussianBlur(4))
    chiling.paste(blured_watch, (220, 690))
    chiling.show()
  5. 生成镜像。

    chiling2 = chiling.transpose(Image.FLIP_LEFT_RIGHT)
    chiling2.show()
  6. 生成缩略图。

    width, height = chiling.size
    width, height = int(width * 0.4), int(height * 0.4)
    chiling.thumbnail((width, height))
  7. 合成图片。

    frame = Image.open('frame.jpg')
    frame.show()
    frame.paste(chiling, (210, 150))
    frame.paste(chiling2, (522, 150))
    frame.show()

上面的知识在Python-100-Days项目的第15天中也有对应的内容。

向微信好友群发祝福视频#

  1. 安装itchat三方库。

    itchat是一个开源的微信个人号接口,使用Python调用微信从未如此简单。

    Terminal window
    pip install itchat

    或

    Terminal window
    pip3 install itchat
  2. 登录微信。

    import itchat
    itchat.auto_login()

    说明:用自己的微信扫描屏幕上出现的二维码就完成了登录操作,登录之后才能获取自己的好友信息以及发送消息给自己的好友。

  3. 查找自己的朋友。

    friends_list = itchat.get_friends(update=True)
    print(len(friends_list))
    luohao = friends_list[0]
    props = ['NickName', 'Signature', 'Sex']
    for prop in props:
    print(luohao[prop])

    说明:friends_list相当于是一个列表,列表中的第一个元素是自己。

  4. 随机选出5个朋友,获得他们的用户名、昵称、签名。

    lucky_friends = random.sample(friends_list[1:], 5)
    props = ['NickName', 'Signature', 'City']
    for friend in lucky_friends:
    for prop in props:
    print(friend[prop] or '没有此项信息')
    print('-' * 80)
  5. 给朋友发送文字消息。

    itchat.send_msg('急需一个红包来拯救堕落的灵魂!!!', toUserName='@8e06606db03f0e28d0ff884083f727e6')
  6. 群发视频给幸运的朋友们。

    lucky_friends = random.sample(friends_list[1:], 5)
    for friend in lucky_friends:
    username = friend['UserName']
    itchat.send_video('/Users/Hao/Desktop/my_test_video.mp4', toUserName=username)

利用itchat还能做很多事情,比如有好友给自己发了消息又撤回了,如果想查看这些被撤回的消息,itchat就可以做到(注册一个接收消息的钩子函数,请参考CSDN上的一篇文章);再比如,有时候我们想知道某个好友有没有把我们删除或者拉入黑名单,也可以利用itchat封装的群聊功能,非好友和黑名单用户不会被拉入群聊,通过创建群聊函数的返回值就可以判定你和指定的人之间的关系。

不用客户端查看热点新闻#

  1. 安装requests库。(点击常看官方文档)

    Terminal window
    pip install requests

    或

    Terminal window
    pip3 install requests
  2. 爬取新闻数据或者通过API接口获取新闻数据。

    import requests
    resp = requests.get('http://api.tianapi.com/allnews/?key=请使用自己申请的Key&col=7&num=50')

    说明:上面使用了天行数据提供的数据接口,需要的话可以自行去天行数据的网站注册开通,调用接口的时候要填写注册成功后系统分配给你的key。

  3. 使用反序列化将JSON字符串解析为字典并获取新闻列表。

    import json
    newslist = json.loads(resp.text)['newslist']
  4. 对新闻列表进行循环遍历,找到感兴趣的新闻,例如:华为。

    for news in newslist:
    title = news['title']
    url = news['url']
    if '华为' in title:
    print(title)
    print(url)
  5. 调用短信网关发送短信到手机上,告知关注的新闻标题并给出链接。

    import re
    pattern = re.compile(r'https*:\/\/[^\/]*\/(?P<url>.*)')
    matcher = pattern.match(url)
    if matcher:
    url = matcher.group('url')
    resp = requests.post(
    url='http://sms-api.luosimao.com/v1/send.json',
    auth=('api', 'key-请使用你自己申请的Key'),
    data={
    'mobile': '13548041193',
    'message': f'发现一条您可能感兴趣的新闻 - {title},详情点击https://news.china.com/{url} 查看。【Python小课】'
    },
    timeout=10,
    verify=False
    )

    说明:上面的代码使用了螺丝帽提供的短信网关服务,利用短信网关发送短信是需要支付费用的,但是一般的平台都会提供若干条免费的测试短信。发送短信必须遵守平台的规则,违规的短信是无法发送的。上面发短信时使用的短信模板(“发现一条您可能感兴趣的新闻 - ###,详情点击https://news.china.com/### 查看。”)和短信签名(“【Python小课】”)需要登录螺丝帽管理平台进行配置,如果不清楚如何配置,可以联系平台的客服人员进行咨询。


02 算法入门系列课程1 - 周而复始#

算法概述#

  1. 什么是算法?

    解决问题的正确方法和具体的实施步骤。

    例子1:如何在两栋相距50m的大楼的两个房间牵一条线(两个房间都有窗)?

    • 养一只鸟(如鸽子),将线送过去
    • 用很长的杆子将线递过去
    • 用无人机(遥控飞行器)将线送过去

    如何评价这些方法的好坏?少花钱,不费事!

    例子2:大教室里坐了几百名学生一起听课,如何快速的统计学生人数?

    例子3:向列表容器中逆向插入100000个元素。

    • 方法1:

      nums = []
      for i in range(100000):
      nums.append(i)
      nums.reverse()
    • 方法2:

      nums = []
      for i in range(100000):
      nums.insert(0, i)

    例子3:生成Fibonacci数列(前100个Fibonacci数)。

    • 方法1 - 递推:

      a, b = 0, 1
      for num in range(1, 101):
      a, b = b, a + b
      print(f'{num}: {a}')
    • 方法2 - 递归:

      def fib(num):
      if num in (1, 2):
      return 1
      return fib(num - 1) + fib(num - 2)
      for num in range(1, 101):
      print(f'{num}: {fib(num)}')
    • 方法3 - 改进的递归:

      def fib(num, temp={}):
      if num in (1, 2):
      return 1
      elif num not in temp:
      temp[num] = fib(num - 1) + fib(num - 2)
      return temp[num]
    • 方法4 - 改进的递归:

      from functools import lru_cache
      @lru_cache()
      def fib(num):
      if num in (1, 2):
      return 1
      return fib(num - 1) + fib(num - 2)
  2. 如何评价算法的好坏?

    渐近时间复杂度和渐近空间复杂度。

  3. 大O符号的意义?

    表示一个函数相对于输入规模的增长速度,也可以称之为函数的数量级。

    大O符号说明例子
    O(c)O(c)常量时间复杂度布隆过滤器 / 哈希存储
    O(log2n)O(log_2n)对数时间复杂度二分查找(折半查找)
    O(n)O(n)线性时间复杂度顺序查找 / 桶排序
    O(n∗log2n)O(n*log_2n)对数线性时间复杂度高级排序算法(归并排序、快速排序)
    O(n2)O(n^2)平方时间复杂度简单排序算法(选择排序、插入排序、冒泡排序)
    O(n3)O(n^3)立方时间复杂度Floyd算法 / 矩阵乘法运算
    O(2n)O(2^n)几何级数时间复杂度汉诺塔
    O(n!)O(n!)阶乘时间复杂度旅行经销商问题

穷举法#

在计算机科学中,穷举法或者暴力搜索法是一个非常非常直观的解决问题的方法,这种方法通过一项一项的列举解决方案所有可能的候选项以及检查每个候选项是否符合问题的描述,最终得到问题的解。

虽然暴力搜索很容易实现,并且如果解决方案存在它就一定能够找到,但是它的代价是和候选方案的数量成比例的,由于这一点,在很多实际问题中,消耗的代价会随着问题规模的增加而快速地增长。因此,当问题规模有限或当存在可用于将候选解决方案的集合减少到可管理大小时,就可以使用暴力搜索。另外,当实现方法的简单度比速度更重要的时候,也可以考虑使用这种方法。

经典例子#

  1. 百钱百鸡问题:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100元买一百只鸡,问公鸡、母鸡、小鸡各有多少只?

    for x in range(21):
    for y in range(34):
    z = 100 - x - y
    if z % 3 == 0 and 5 * x + 3 * y + z // 3 == 100:
    print(x, y, z)
  2. 五人分鱼问题:ABCDE五人在某天夜里合伙捕鱼,最后疲惫不堪各自睡觉。第二天A第一个醒来,他将鱼分为5份,扔掉多余的1条,拿走了属于自己的一份;B第二个醒来,也将鱼分为5份,扔掉多余的1条,拿走属于自己的一份;然后C、D、E依次醒来,也按同样的方式分鱼,问他们至少捕了多少条鱼?

    fish = 6
    while True:
    total = fish
    enough = True
    for _ in range(5):
    if (total - 1) % 5 == 0:
    total = (total - 1) // 5 * 4
    else:
    enough = False
    break
    if enough:
    print(fish)
    break
    fish += 5
  3. 暴力破解口令:

    import re
    import PyPDF2
    with open('Python_Tricks_encrypted.pdf', 'rb') as pdf_file_stream:
    reader = PyPDF2.PdfFileReader(pdf_file_stream)
    with open('dictionary.txt', 'r') as txt_file_stream:
    file_iter = iter(lambda: txt_file_stream.readline(), '')
    for word in file_iter:
    word = re.sub(r'\s', '', word)
    if reader.decrypt(word):
    print(word)
    break

03 算法入门系列2 - 在水一方#

在第一次的公开课中,我们讲到了穷举法。穷举法也被称为暴力搜索法,今天我们要讲的回溯法就是暴力搜索法的一种。接下来我们讲到的很多算法跟“递归”这个概念有或多或少的关系,所以我们先说说“递归”。

现实中的递归#

从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……

野比大雄在房间里,用时光电视看着未来的情况。电视画面中,野比大雄在房间里,用时光电视看着未来的情况。电视画面中,野比大雄在房间里,用时光电视看着未来的情况……

阶乘的递归定义:0!=10! = 1,n!=n∗(n−1)!n!=n*(n-1)! ,使用被定义对象的自身来为其下定义称为递归定义。

德罗斯特效应是递归的一种视觉形式。图中女性手持的物体中有一幅她本人手持同一物体的小图片,进而小图片中还有更小的一幅她手持同一物体的图片……

递归的应用#

在程序中,一个函数如果直接或者间接的调用了自身,我们就称之为递归函数。

写递归函数有两个要点:

  1. 收敛条件 - 什么时候结束递归。
  2. 递归公式 - 每一项与前一项(前N项)的关系。

例子1:求阶乘。

def fac(num):
if num == 0:
return 1
return num * fac(num - 1)

Python对递归的深度加以了限制(默认1000层函数调用),如果想突破这个限制,可以使用下面的方法。

import sys
sys.setrecursionlimit(10000)

例子2:爬楼梯 - 楼梯有n个台阶,一步可以走1阶、2阶或3阶,走完n个台阶共有多少种不同的走法。

def climb(num):
if num == 0:
return 1
elif num < 0:
return 0
return climb(num - 1) + climb(num - 2) + climb(num - 3)

注意:上面的递归函数性能会非常的差,因为时间复杂度是几何级数级的。

优化后的代码。

from functools import lru_cache
@lru_cache()
def climb(num):
if num == 0:
return 1
elif num < 0:
return 0
return climb(num - 1) + climb(num - 2) + climb(num - 3)

不使用的递归的代码。

def climb(num):
a, b, c = 1, 2, 4
for _ in range(num - 1):
a, b, c = b, c, a + b + c
return a

重点:有更好的办法的时候,请不要考虑递归。

回溯法#

回溯法是暴力搜索法中的一种。对于某些计算问题而言,回溯法是一种可以找出所有(或一部分)解的一般性算法,尤其适用于约束满足问题(在解决约束满足问题时,我们逐步构造更多的候选解,并且在确定某一部分候选解不可能补全成正确解之后放弃继续搜索这个部分候选解本身及其可以拓展出的子候选解,转而测试其他的部分候选解)。

经典案例#

例子1:迷宫寻路。

"""
迷宫寻路
"""
import random
import sys
WALL = -1
ROAD = 0
ROWS = 10
COLS = 10
def find_way(maze, i=0, j=0, step=1):
"""走迷宫"""
if 0 <= i < ROWS and 0 <= j < COLS and maze[i][j] == 0:
maze[i][j] = step
if i == ROWS - 1 and j == COLS - 1:
print('=' * 20)
display(maze)
sys.exit(0)
find_way(maze, i + 1, j, step + 1)
find_way(maze, i, j + 1, step + 1)
find_way(maze, i - 1, j, step + 1)
find_way(maze, i, j - 1, step + 1)
maze[i][j] = ROAD
def reset(maze):
"""重置迷宫"""
for i in range(ROWS):
for j in range(COLS):
num = random.randint(1, 10)
maze[i][j] = WALL if num > 7 else ROAD
maze[0][0] = maze[ROWS - 1][COLS - 1] = ROAD
def display(maze):
"""显示迷宫"""
for row in maze:
for col in row:
if col == -1:
print('■', end=' ')
elif col == 0:
print('□', end=' ')
else:
print(f'{col}'.ljust(2), end='')
print()
def main():
"""主函数"""
maze = [[0] * COLS for _ in range(ROWS)]
reset(maze)
display(maze)
find_way(maze)
print('没有出路!!!')
if __name__ == '__main__':
main()

**说明:**上面的代码用随机放置围墙的方式来生成迷宫,更好的生成迷宫的方式请参考《简单的使用回溯法生成 Tile Based 迷宫》一文。

例子2:骑士巡逻 - 国际象棋中的骑士(马),按照骑士的移动规则走遍整个棋盘的每一个方格,而且每个方格只能够经过一次。

"""
骑士巡逻
"""
import sys
SIZE = 8
def display(board):
"""显示棋盘"""
for row in board:
for col in row:
print(f'{col}'.rjust(2, '0'), end=' ')
print()
def patrol(board, i=0, j=0, step=1):
"""巡逻"""
if 0 <= i < SIZE and 0 <= j < SIZE and board[i][j] == 0:
board[i][j] = step
if step == SIZE * SIZE:
display(board)
sys.exit(0)
patrol(board, i + 1, j + 2, step + 1)
patrol(board, i + 2, j + 1, step + 1)
patrol(board, i + 2, j - 1, step + 1)
patrol(board, i + 1, j - 2, step + 1)
patrol(board, i - 1, j - 2, step + 1)
patrol(board, i - 2, j - 1, step + 1)
patrol(board, i - 2, j + 1, step + 1)
patrol(board, i - 1, j + 2, step + 1)
board[i][j] = 0
def main():
"""主函数"""
board = [[0] * SIZE for _ in range(SIZE)]
patrol(board)
if __name__ == '__main__':
main()

例子3:八皇后 - 如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

说明:这个问题太经典了,网上有大把的答案,留给大家自己搞定。


04 年薪50W+的Python程序员如何写代码#

为什么要用Python写代码#

没有对比就没有伤害#

很多互联网和移动互联网企业对开发效率的要求高于对执行效率的要求。

例子1:hello, world#

C的版本:

#include <stdio.h>
int main() {
printf("hello, world\n");
return 0;
}

Java的版本:

class Example01 {
public static void main(String[] args) {
System.out.println("hello, world");
}
}

Python的版本:

print('hello, world')
例子2:1-100求和#

C的版本:

#include <stdio.h>
int main() {
int total = 0;
for (int i = 1; i <= 100; ++i) {
total += i;
}
printf("%d\n", total);
return 0;
}

Python的版本:

print(sum(range(1, 101)))
例子3:创建和初始化数组(列表)#

Java的版本:

import java.util.Arrays;
public class Example03 {
public static void main(String[] args) {
boolean[] values = new boolean[10];
Arrays.fill(values, true);
System.out.println(Arrays.toString(values));
int[] numbers = new int[10];
for (int i = 0; i < numbers.length; ++i) {
numbers[i] = i + 1;
}
System.out.println(Arrays.toString(numbers));
}
}

Python的版本:

values = [True] * 10
print(values)
numbers = [x for x in range(1, 11)]
print(numbers)
例子4:双色球随机选号#

Java的版本:

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
import java.util.concurrent.ThreadLocalRandom;
class Example03 {
private static final List<Integer> RED_BALLS = new ArrayList<>();
static {
for (int i = 1; i <= 33; ++i) RED_BALLS.add(i);
}
public static void display(List<Integer> balls) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < balls.size(); i++) {
sb.append(String.format("%02d ", balls.get(i)));
if (i == 5) sb.append("| ");
}
System.out.println(sb.toString());
}
public static List<Integer> generate() {
List<Integer> pool = new ArrayList<>(RED_BALLS);
Collections.shuffle(pool);
List<Integer> selectedBalls = pool.subList(0, 6);
Collections.sort(selectedBalls);
selectedBalls.add(ThreadLocalRandom.current().nextInt(1, 17));
return selectedBalls;
}
public static void main(String[] args) {
try (Scanner sc = new Scanner(System.in)) {
System.out.print("机选几注: ");
if (sc.hasNextInt()) {
int num = sc.nextInt();
for (int i = 0; i < num; ++i) {
display(generate());
}
}
}
}
}

Python的版本:

import random
RED_RANGE = range(1, 34)
BLU_RANGE = range(1, 17)
def generate():
red_balls = random.sample(RED_RANGE, 6)
red_balls.sort()
blue_ball = random.choice(BLU_RANGE)
return red_balls, blue_ball
def display(sample):
reds, blue = sample
reds_str = ' '.join(f'{ball:02d}' for ball in reds)
print(f'{reds_str} | {blue:02d}')
num = int(input('机选几注: '))
for _ in range(num):
display(generate())

温馨提示:珍爱生命,远离任何形式的赌博。

例子5:实现一个简单的HTTP服务器。#

Java的版本:

说明:JDK 1.6以前,需要通过套接字编程来实现,具体又可以分为多线程和NIO两种做法。JDK 1.6以后,可以使用com.sun.net.httpserver包提供的HttpServer类来实现。

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
class Example05 {
public static void main(String[] arg) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/", new RequestHandler());
server.start();
}
static class RequestHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String response = "<h1>hello, world</h1>";
exchange.sendResponseHeaders(200, 0);
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
}
}
}

Python的版本:

from http.server import HTTPServer, SimpleHTTPRequestHandler
class RequestHandler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write('<h1>hello, world</h1>'.encode())
server = HTTPServer(('', 8000), RequestHandler)
server.serve_forever()

或

python3 -m http.server 8000
一行Python代码可以做什么#

很多时候,你的问题只需一行Python代码就能解决。

# 一行代码实现求阶乘函数
fac = lambda x: __import__('functools').reduce(int.__mul__, range(1, x + 1), 1)
# 一行代码实现求最大公约数函数
gcd = lambda x, y: y % x and gcd(y % x, x) or x
# 一行代码实现判断素数的函数
is_prime = lambda x: x > 1 and not [f for f in range(2, int(x ** 0.5) + 1) if x % f == 0]
# 一行代码实现快速排序
quick_sort = lambda items: len(items) and quick_sort([x for x in items[1:] if x < items[0]]) + [items[0]] + quick_sort([x for x in items[1:] if x > items[0]]) or items
# 生成FizzBuzz列表
['Fizz'[x % 3 * 4:] + 'Buzz'[x % 5 * 4:] or x for x in range(1, 101)]
设计模式从未如此简单#

Python是动态类型语言,大量的设计模式在Python中被简化或弱化。

思考:如何优化下面的代码。

def fib(num):
if num in (1, 2):
return 1
return fib(num - 1) + fib(num - 2)

代理模式在Python中可以通过内置的或自定义的装饰器来实现。

from functools import lru_cache
@lru_cache()
def fib(num):
if num in (1, 2):
return 1
return fib(num - 1) + fib(num - 2)
for n in range(1, 121):
print(f'{n}: {fib(n)}')

说明:通过Python标准库functools模块的lru_cache装饰器为fib函数加上缓存代理,缓存函数执行的中间结果,优化代码的性能。

单例模式在Python中可以通过自定义的装饰器或元类来实现。

from functools import wraps
from threading import RLock
def singleton(cls):
instances = {}
lock = RLock()
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
with lock:
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]

说明:需要实现单例模式的类只需要添加上面的装饰器即可。

原型模式在Python中可以通过元类来实现。

import copy
class PrototypeMeta(type):
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
cls.clone = lambda self, is_deep=True: \
copy.deepcopy(self) if is_deep else copy.copy(self)

说明:通过元类给指定了metaclass=PrototypeMeta的类添加一个clone方法实现对象克隆,利用Python标准库copy模块的copy和deepcopy分别实现浅拷贝和深拷贝。

数据采集和数据分析从未如此简单#

网络数据采集是Python最擅长的领域之一。

例子:获取豆瓣电影“Top250”。

import random
import time
import requests
from bs4 import BeautifulSoup
for page in range(10):
resp = requests.get(
url=f'https://movie.douban.com/top250?start={25 * page}',
headers={'User-Agent': 'BaiduSpider'}
)
soup = BeautifulSoup(resp.text, "lxml")
for elem in soup.select('a > span.title:nth-child(1)'):
print(elem.text)
time.sleep(random.random() * 5)

利用NumPy、Pandas、Matplotlib可以轻松实现数据分析和可视化。

写出Python代码的正确姿势#

用Python写代码就要写出Pythonic的代码。

姿势1:选择结构的正确姿势#

跨界开发者的代码:

name = 'jackfrued'
fruits = ['apple', 'orange', 'grape']
owners = {'name': '骆昊', 'age': 40, 'gender': True}
if name != '' and len(fruits) > 0 and len(owners.keys()) > 0:
print('Jackfrued love fruits.')

Pythonic的代码:

name = 'jackfrued'
fruits = ['apple', 'orange', 'grape']
owners = {'name': '骆昊', 'age': 40, 'gender': True}
if name and fruits and owners:
print('Jackfrued love fruits.')
姿势2:交换两个变量的正确姿势#

跨界开发者的代码:

temp = a
a = b
b = temp

或

a = a ^ b
b = a ^ b
a = a ^ b

Pythonic的代码:

a, b = b, a
姿势3:用序列组装字符串的正确姿势#

跨界开发者的代码:

chars = ['j', 'a', 'c', 'k', 'f', 'r', 'u', 'e', 'd']
name = ''
for char in chars:
name += char

Pythonic的代码:

chars = ['j', 'a', 'c', 'k', 'f', 'r', 'u', 'e', 'd']
name = ''.join(chars)
姿势4:遍历列表的正确姿势#

跨界开发者的代码:

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
index = 0
for fruit in fruits:
print(index, ':', fruit)
index += 1

Pythonic的代码:

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
for index, fruit in enumerate(fruits):
print(index, ':', fruit)
姿势5:创建列表的正确姿势#

跨界开发者的代码:

data = [7, 20, 3, 15, 11]
result = []
for i in data:
if i > 10:
result.append(i * 3)

Pythonic的代码:

data = [7, 20, 3, 15, 11]
result = [num * 3 for num in data if num > 10]
姿势6:确保代码健壮性的正确姿势#

跨界开发者的代码:

data = {'x': '5'}
if 'x' in data and isinstance(data['x'], (str, int, float)) \
and data['x'].isdigit():
value = int(data['x'])
print(value)
else:
value = None

Pythonic的代码:

data = {'x': '5'}
try:
value = int(data['x'])
print(value)
except (KeyError, TypeError, ValueError):
value = None

使用Lint工具检查你的代码规范#

阅读下面的代码,看看你能看出哪些地方是有毛病的或者说不符合Python的编程规范的。

from enum import *
@unique
class Suite (Enum):
SPADE, HEART, CLUB, DIAMOND = range(4)
class Card(object):
def __init__(self,suite,face ):
self.suite = suite
self.face = face
def __repr__(self):
suites='♠♥♣♦'
faces=['','A','2','3','4','5','6','7','8','9','10','J','Q','K']
return f'{suites[self.suite.value]}{faces[self.face]}'
import random
class Poker(object):
def __init__(self):
self.cards =[Card(suite, face) for suite in Suite
for face in range(1, 14)]
self.current=0
def shuffle (self):
self.current=0
random.shuffle(self.cards)
def deal (self):
card = self.cards[self.current]
self.current+=1
return card
def has_next (self):
if self.current<len(self.cards): return True
return False
p = Poker()
p.shuffle()
print(p.cards)
PyLint的安装和使用#

Pylint是Python代码分析工具,它分析Python代码中的错误,查找不符合代码风格标准(默认使用的代码风格是 PEP 8)和有潜在问题的代码。

Terminal window
pip install pylint
pylint [options] module_or_package

Pylint输出格式如下所示。

模块名:行号:列号: 消息类型 消息

消息类型有以下几种:

  1. C - 惯例:违反了Python编程惯例(PEP 8)的代码。
  2. R - 重构:写得比较糟糕需要重构的代码。
  3. W - 警告:代码中存在的不影响代码运行的问题。
  4. E - 错误:代码中存在的影响代码运行的错误。
  5. F - 致命错误:导致Pylint无法继续运行的错误。

Pylint命令的常用参数:

  1. --disable=<msg ids>或-d <msg ids>:禁用指定类型的消息。
  2. --errors-only或-E:只显示错误。
  3. --rcfile=<file>:指定配置文件。
  4. --list-msgs:列出Pylint的消息清单。
  5. --generate-rcfile:生成配置文件的样例。
  6. --reports=<y_or_n>或-r <y_or_n>:是否生成检查报告。

使用Profile工具剖析你的代码性能#

cProfile模块#

example01.py

import cProfile
def is_prime(num):
for factor in range(2, int(num ** 0.5) + 1):
if num % factor == 0:
return False
return True
class PrimeIter:
def __init__(self, total):
self.counter = 0
self.current = 1
self.total = total
def __iter__(self):
return self
def __next__(self):
if self.counter < self.total:
self.current += 1
while not is_prime(self.current):
self.current += 1
self.counter += 1
return self.current
raise StopIteration()
cProfile.run('list(PrimeIter(10000))')

执行结果:

114734 function calls in 0.573 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.006 0.006 0.573 0.573 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 example.py:14(__init__)
1 0.000 0.000 0.000 0.000 example.py:19(__iter__)
10001 0.086 0.000 0.567 0.000 example.py:22(__next__)
104728 0.481 0.000 0.481 0.000 example.py:5(is_prime)
1 0.000 0.000 0.573 0.573 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
line_profiler#

给需要剖析时间性能的函数加上一个profile装饰器,这个函数每行代码的执行次数和时间都会被剖析。

example02.py

@profile
def is_prime(num):
for factor in range(2, int(num ** 0.5) + 1):
if num % factor == 0:
return False
return True
class PrimeIter:
def __init__(self, total):
self.counter = 0
self.current = 1
self.total = total
def __iter__(self):
return self
def __next__(self):
if self.counter < self.total:
self.current += 1
while not is_prime(self.current):
self.current += 1
self.counter += 1
return self.current
raise StopIteration()
list(PrimeIter(1000))

安装和使用line_profiler三方库。

Terminal window
pip install line_profiler
kernprof -lv example.py
Wrote profile results to example02.py.lprof
Timer unit: 1e-06 s
Total time: 0.089513 s
File: example02.py
Function: is_prime at line 1
# Hits Time Per Hit % Time Line Contents
==============================================================
1 @profile
2 def is_prime(num):
3 86624 43305.0 0.5 48.4 for factor in range(2, int(num ** 0.5) + 1):
4 85624 42814.0 0.5 47.8 if num % factor == 0:
5 6918 3008.0 0.4 3.4 return False
6 1000 386.0 0.4 0.4 return True
memory_profiler#

给需要剖析内存性能的函数加上一个profile装饰器,这个函数每行代码的内存使用情况都会被剖析。

example03.py

@profile
def eat_memory():
items = []
for _ in range(1000000):
items.append(object())
return items
eat_memory()

安装和使用memory_profiler三方库。

pip install memory_profiler
python3 -m memory_profiler example.py
Filename: example03.py
Line # Mem usage Increment Line Contents
================================================
1 38.672 MiB 38.672 MiB @profile
2 def eat_memory():
3 38.672 MiB 0.000 MiB items = []
4 68.727 MiB 0.000 MiB for _ in range(1000000):
5 68.727 MiB 1.797 MiB items.append(object())
6 68.727 MiB 0.000 MiB return items

如何构建综合职业素养#

学习总结#
  1. 了解全局
  2. 确定范围
  3. 定义目标
  4. 寻找资源
  5. 创建学习计划
  6. 筛选资源
  7. 开始学习,浅尝辄止(YAGNI)
  8. 动手操作,边学边玩
  9. 全面掌握,学以致用
  10. 乐为人师,融会贯通
时间管理#
  1. 提升专注力

  2. 充分利用碎片时间

  3. 使用番茄工作法

  4. 时间是怎么浪费掉的

  5. 任何行动都比不采取行动好

好书推荐#
  1. 职业规划:《软技能 - 代码之外的生存指南》
  2. 吴军系列:《浪潮之巅》、《硅谷之谜》、《数学之美》、……
  3. 时间管理:《成为一个更高效的人》、《番茄工作法图解》

Python100天

已完结

第 11 篇,共 11 篇本文属于系列

公开课 教程全文(4 篇合并)
https://www.klucen.cn/posts/python/open-class-full/
作者
骆昊(jackfrued)
发布于
2025-12-14

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

评论

正在加载评论...

最后更新于 ,距今已过 301 天

部分内容可能已过时