easy-py

本tutorial来自蛇皮书,网上各种教程cookbook,以及lc刷题时想到的查漏补缺(感觉太无聊不刷了)

不一定全部正确,也不一定适合大家

只是我入门时拿来用的,也顺便当作一个笔记随时更新

变量

py在程序中可随时修改变量的值,并且始终记录变量的最新值(无需声明类型)

message = "apple"
print(message)
message = 233.33
print(message)
# 一段令C++选手感到匪夷所思的代码

可以用type(elem)来查看elem的类型,

注意以下区别

type("str") # <class 'str'>
type(str)   # <class 'type'>
type(type)  # <class 'type'>

字符串

字符串可以用""或者''来表示,区别在于两者的字符转义,比如"'"

.title() 首字母大写

.upper() .lower() 大小写转换

+ 合并字符串

len(str)返回长度

.rstrip() 删除串末尾的空白,同理.lstrip()删除头部的空白,.strip()两端删除,也可以用如下方法替代

s = "   dsu on tree   "
print(s.lstrip().rstrip())
# 输出dsu on tree

注意字符串不可以像C++那样修改s[i],可以用.replace(old,new[,max])来进行修改(注意赋值)

s = "111222333"
print(s.replace("1","4"))
print(s)
s = s.replace("1","4",2)
print(s)
# 444222333
# 111222333
# 441222333

.split()可以把空格当作分隔符将字符串拆成多段,并返回一个列表

(默认情况下,也可以按照传入字符串参数自定义)

ord(char)可以查看字符的编码值

注意python获取ascii码不可用'9'-'0'这种方法

另外,字符串前缀u,r,b有特殊含义

u 指代unicode字符串(py3默认)

r 指代非转义字符串

b 指代bytes

print(r"\n\n\n")
# \n\n\n

数字

python中的整型均为封装好的高精度(乘法使用karatsuba实现)

val = 1<<128
print(val)
val = val>>126
print(val)
# 输出340282366920938463463374607431768211456和4

str(val) 把整型转换为字符串

str1 = "a dog with"
val  = 1<<16
str2 = "legs."
print(str1+' '+str(val)+' '+str2)
# 输出a dog with 65536 legs.

以及/是普通的除法,哪怕是整型也可以返回浮点

要实现向下整除需要//

print(3/3)
# 1.0
print(3//3)
# 1
print(2/3)
# 0.6666666666666666
print(2//3)
# 0

浮点满足IEEE754,勉强能用的约17位,高精度可用decimal模块,这里省略

另外,python的浮点原生支持朴素的乘方运算**,和%运算

列表

基本用法

lis = [1,'1',1926.0817]
print(lis)
print(lis[2])
# 创建空的list可以 lis = []

list可简单理解成可容纳多个类型的vector,因此[]访问是$O(1)$的时间复杂度,可修改,可反向如[-1]访问

.append(val) 末尾添加新的元素

.pop() 末尾弹出元素(带返回值),其实可以.pop(pos)

.insert(pos,val)pos位添加元素,时间$O(n)​$

.del(pos)pos位删除元素,与.pop(pos)区别在于前者没有返回值

.remove(val) 删除第一个值为val的元素

.clear() 清空所有元素,注意与del list的不同

组织列表

.sort()sorted()的区别在于前者是原地排序,后者返回临时列表,可以调用sort(reverse=True)进行反向排序,但排序的前提是存在操作符<的定义

lis = ["5","12","1"]
print(sorted(lis,reverse=True))

.reverse()原地列表翻转

len(list) $O(1)$返回列表长度

遍历列表

python对缩进要求严格,一般遍历操作如下

lis = ["stayNight","hollowAtaraxia","unlimitedCodes"]
for elem in lis:
    print(elem,end=' ')

或者使用range(lo,hi),注意范围是[lo,hi)

lis = []
for elem in range(1,3):
    lis.append(elem)
    lis.append(elem<<2)
print(lis)
# 输出[1, 4, 2, 8]

另外range(lo,hi[,step])可直接用于列表的构造,step是步进

lis = list(range(0,5))
print(lis)
# 输出[0, 1, 2, 3, 4]
lis = list(range(0,5,2))
print(lis)
# 输出[0, 2, 4]

如果需要优雅地遍历列表且能表示下标,可以如下

# Leetcode - 1
class Solution:
    def twoSum(self, nums, target):
        dic = {} # 字典的构造,现在可以忽略
        for i,elem in enumerate(nums): #i为下标,elem为值,列表需要enumerate
            if dic.__contains__(target-elem) == True:
                return [dic[target-elem],i]
            dic[elem]=i

ps.enumerate可以定义起始位置

一些简单计数方法

min(list),max(list),sum(list)

不用说明了吧

列表推导式(列表解析)

可以通过简单的式子构造出列表

其实就是把循环和创建元素(的规则)合并在一起,返回一个列表

lis = [2,4,6]
lis2 = [val*2 for val in lis]
print(lis2)
# 输出[4, 8, 12]
lis3 = [val*2 for val in range(1,4)]
print(lis3)
# 输出[2, 4, 6]

处理切片

切片是指列表中的部分元素,可以用[lo:hi]表示,起始索引为lo,终止索引为hi,同样注意是[lo,hi),并且可省略任一参数

样例如下

nichijou = ["nano","hakase","sakamoto","mio","yuko"]
print(nichijou[1:3])
# ['hakase', 'sakamoto']
print(nichijou[:2])
# ['nano', 'hakase']
print(nichijou[2:])
# ['sakamoto', 'mio', 'yuko']
print(nichijou[:-3])
# ['nano', 'hakase']
print(nichijou[-3:])
# ['sakamoto', 'mio', 'yuko']
print(nichijou[:])
# ['nano', 'hakase', 'sakamoto', 'mio', 'yuko']

既然切片也是列表那也可以进行列表该有的操作,以遍历为例

nichijou = ["nano","hakase","sakamoto","mio","yuko"]
shinonome = []
for elem in nichijou[0:3]:
    shinonome.append(elem)
print(shinonome)
# 输出['nano', 'hakase', 'sakamoto']

复制列表

复制同样要注意引用的问题,既不要直接取=

一个简洁的方法是利用临时生成的列表,比如foo=list[:]

lis = [1,2,3,4,5]
tmp = lis # 两个变量指向同一个列表
tmp[4]=6
print(lis)
lis = [1,2,3,4,5]
tmp = lis[:]
tmp[4]=6
print(lis)
# [1, 2, 3, 4, 6]
# [1, 2, 3, 4, 5] 避免了引用带来的麻烦

另外,更为通用的方法是标准库提供的copy模块

对于不可变类型,无论深浅拷贝,地址都是一样的

对于可变类型才有所区分,待更新

元组

元组可看作是不可变的列表,用()来构造

注意元组不能单个修改(不可变),但可以整个变量赋值

tup = (1,2,'a')
print(tup)
tup = (4,5) # 这是合法的
print(tup)
tup = (1,)  # 防止构造上的歧义

另外,元组没有元组推导式,返回的仅是迭代器

解压序列

当变量的数量与序列的元素个数相等时,可进行解压并给变量赋值

lis = [1,5.0,"chars",[5,3,2]]
a,b,c,[d,e,f] = lis
print(f) # 2

以及

lis = [1,5.0,"chars",[5,3,2]]
a,b,c,d = lis
print(d) # [5,3,2]

这种功能需要可迭代对象才能实现(比如字符串也行)

更灵活的应用可使用星号表达式,如

lis = [1,5.0,"chars",[5,3,2]]
a,*b,c = lis
print(b) # [5.0, 'chars']

注意变量b永远是列表,即使元素个数为0

另一个示例

records = [
    ('foo', 1, 2),
    ('bar', 'hello'),
    ('foo', 3, 4),
]

def do_foo(x, y):
    print('foo', x, y)

def do_bar(s):
    print('bar', s)

for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)

遗留问题

Q.如何优雅地创建指定大小的列表 Tips.列表推导式

Q.如何优雅地构造多维列表

Q.range的内部实现

条件判断

if 语句

使用样例如下

money = 20000
if money > 2000:
    print("Good")
    print("I am so rich")
elif money > 100:
    print("Ok")
else:
    print("NO!")

逻辑符

python没有C的&&||,分别用andor替代

并且布尔值是TrueFalse

if 1>0 and 1>-1:
    print(True)

也可以直接连判

if 0<1<2:
    print(True)

检查特定值

python可以用innot in进行特定值的判断

lis = [1,2,4]
print(3 in lis)
print(5 not in lis)

判等

判等可通过==is进行判断

两者区别在于==只对值进行验证而不关心对象的关系,而is必须是同一内存空间

字典

字典通过{}来构造,用键访问值

dic = {"A":0,1:1}
print(dic["A"])

好看点的构造

dic={
    "J":"JOJO",
    "D":"DIO",
    "G":"GCQ"
}

字典的添加,修改和删除操作

dic={}
dic["a"]=1
dic["b"]=2
dic["a"]=3
print(dic)
# 输出{'a': 3, 'b': 2}
del dic["a"]
print(dic)
# 输出{'b': 2}

字典的遍历

dic={
    "J":"JOJO",
    "D":"DIO",
    "O":"OLAOLA"
}
for key,val in dic.items():
    print(key,val)
# J JOJO
# D DIO
# O OLAOLA
for key in dic.keys():
    print(key,end=" ")
# J D O 
for val in dic.values():
    print(val,end=" ")
# JOJO DIO OLAOLA 

.values()中可能会用重复的值,此时可以用set()来生成一个不重复的集合

dic={
    "J":"JOJO",
    "j":"JOJO",
    "D":"DIO",
    "O":"OLAOLA"
}
for val in dic.values():
    print(val,end=" ")
print("")
for val in set(dic.values()):
    print(val,end=" ")
# JOJO JOJO DIO OLAOLA 
# DIO JOJO OLAOLA 

按顺序遍历

dic={
    "J":"JOJO",
    "D":"DIO",
    "O":"OLAOLA"
}
for key in sorted(dic.keys()):
    print(key,end=" ")
# 输出D J O 

判断是否有key,查阅资料得到.__contains__(key),当然也可以用in来判断

注意dict在访问不存在的key时会报错,这个时候可以使用defaultdict来代替,它能返回一个默认值

IO

标准输入

python使用input()进行输入,返回的是字符串,必要时需要进行类型转换

age=int(input())

可以对输入进行提示

name=input("Tell me your name: ")

EOF

python并没有C的EOF判断,但可以用处理异常的try-except实现等价的功能

while True:
    try:
        s=input()
    except:
        break

格式化输出

print(str.format(...))可以实现类似C的printf

更多具体用法如下

name, year = "Python", 1990
print("%s was born in %s." % (name, year))
print("{} was born in {}.".format(name, year))
print(f"{name} was born in {year}.")

注意中文可能会有乱码

简单粗暴的解决方法是加上

import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')

文件读写

打开文件使用open(fileName[,mode]),关闭文件则需要close(fileName)

其中,fileName是一个指代文件名(可包含路径)的字符串,mode"r","w","r+","a"模式,分别代表只读,只写,可读写,附加模式

open(fileName)默认为只读,并且读入时文件必须存在,写入模式下会在没有文件的前提创建新的文件,或者已存在文件时清除所有内容

注意Windows下的文件路径用反斜杠\,Linux则需要斜杠/

如果明确文件只是在某个局部使用,可以用with语句配合open(fileName)打开文件,表示超出该语句范围后文件自动关闭,fp可粗略理解成一个文件对象

inp = 'stdin.txt'
with open(inp) as fp:
    all = fp.read() # read()读入所有内容
    print(all.rstrip())
    # .rstrip()可处理末尾多出来的空行

逐行输出可以通过遍历文件对象实现

需要注意的是文本原来就有\n换行,而python中的print也是自带换行,导致一行就输出\n\n,因此要么使用.rstrip(),要么使用end=""

inp = 'stdin.txt'
with open(inp) as fp:
    for line in fp:
        print(line,end="")

通过文件创建列表

inp = 'stdin.txt'
with open(inp) as fp:
    lis = fp.readlines()
print(lis)
# 假定stdin.txt包含
# 1 2 3
# 4 5
# 6
# 那么lis则包含
# ['1 2 3\n', '4 5\n', '6\n']

因此列表中单个元素的打印还得需要.rstrip()

文件的写入可以用.write(str)方法

inp = 'stdin.txt'
with open(inp,"a") as fp:
    fp.write("7 8 9\n")

函数

声明

函数用def来声明,比如

def f():
    return 1

带默认值的用法

def f(fir,las=0):
    return fir-las

接收任意数量的实参

def f(*arr):
    print(arr)
f("nano","hakase","sakamoto")
# 输出('nano', 'hakase', 'sakamoto')
# 注意打包的是元组

当然也可以结合起来

def f(size,*arr):

当函数接受**时,返回的是字典

def f(**dic):
    print(dic)
f(key1=2,key2=4)
# {'key1': 2, 'key2': 4}

另外,函数内声明全局变量要使用global

def f(u):
    global v
    v = u

f(-1)
print(v) # -1
f(~-1)
print(v) # 0

调用

除了一般的调用,还可以使用关键字方式

def f(fir,las=0):
    return fir-las
print(f(las=100,fir=0))
# 输出-100

传入引用

def f(lis):
    lis.append(1)
lis=[]
f(lis)
print(lis)
# 输出[1]

禁止副作用,传入副本

def f(lis):
    lis.append(1)
lis=[]
f(lis[:])
print(lis)
# 输出[]

***

def f(*arr):
    print(arr)

f([1,2,3]) # ([1, 2, 3],)
f(*[1,2,3]) # (1, 2, 3)
def f(**dic):
    print(dic)

dic = {"1":2,"3":4}
f(d=dic) # {'d': {'1': 2, '3': 4}}
f(**dic) # {'1': 2, '3': 4}

个人感觉这种写法很沙雕,只是了解一下好了

将函数存入模块

模块.py为后缀的文件,如果已存在文件名tmp.py,就可以导入

import tmp
print(tmp.val) #若在tmp.py已存在val

导入特定的值或函数

from tmp import val,fun
print(fun(val))

as可用于起别名

from tmp import val as v
print(v)

*导入所有函数和值

from tmp import *

lambda

lambda函数是一种匿名函数,可接受多个参数,但只有一条表达式

mid = lambda lo,hi : lo+(hi-lo)//2
print(mid(1,5)) # 然而看着并不匿名

看看更匿名的做法

def f(n,add):
    return add(n)

print(f(5,lambda x:x+1)) # 6

lambda也可以这么用

def fun(val):
    return lambda a : a*val
fun0 = fun(2)
fun1 = fun(3)
print(fun0(4))
print(fun1(4))

闭包

def f():
    n = 0
    def add():
        nonlocal n
        n += 1
        print(n)
    return add

tmp = f()

tmp() # 1
tmp() # 2

魔术方法

简单说没啥好说,详细说又太难,暂时搁置

构造

__init__为任意一个类的构造方法,初始化自身时参数必须有self

class Human():
    def __init__(self,sex,height):
        self.sex=sex
        self.height=height

man = Human("male",178)
print(man.sex,man.height)

type作为类的根也可用于构造动态类

Xjb = type('Xjb',(object,),{'a':1,'b':2})
# arg0:类名 arg1:父类 arg2:dict()
xjb = Xjb()
print(xjb.a) # 1

另外,构造方法在更深一层面是由__new__完成

私有和保护

形如__fun的命名为私有方法,变量同理

形如_fun的命名方法为保护方法

class Human():
    def __init__(self,sex,height):
        self.__sex=sex
        self.__height=height
    def getSex(self):
        return self.__sex

man = Human("male",178)
print(man.getSex())

继承

子类继承需要在声明class时传入父类,并且构造方法内使用super()可调用父类的构造方法

class Human():
    def __init__(self,sex,height):
        self.__sex=sex
        self.__height=height
    def getSex(self):
        return self.__sex

class Student(Human):
    def __init__(self,sex,height,school):
        super().__init__(sex,height)
        self.__school=school
    def getSchool(self):
        return self.__school
stu = Student("male",178,"SCP_Foundation")
print(stu.getSex(),stu.getSchool())

重写父类

class Human():
    def __init__(self,sex,height):
        self.sex=sex
        self.__height=height
    def getSex(self):
        return self.sex

class Student(Human):
    def __init__(self,sex,height,school):
        super().__init__(sex,height)
        self.__school=school
    def getSchool(self):
        return self.__school
    def getSex(self):
        print("You are",self.sex)
stu = Student("male",178,"SCP_Foundation")
stu.getSex() # 私有成员__Sex在重载时无法访问

对象

id(obj) 查看对象的内存地址

type(obj) 查看对象的类型

= 指向一个引用

p = print
p("toho")
p(p==print)
p = 6
print(p)

# toho
# True
# 6

GUI编程

python标准库中自带turtle模块可进行GUI编程

至于为什么叫turtle可能是绘制的动作像海龟爬来爬去一样慢

demo

cmd下键入

python -m turtle

以及

python -m turtledemo

(看着还挺爽的)

用例

import turtle as tt
tt.TurtleScreen._RUNNING = True  # 启动绘图
cnt = 0
while cnt < 5:
    tt.forward(200) # 前进200pixel
    tt.right(144)   # 右转144degree
    cnt += 1
tt.done()  # 结束绘图,这将不会关闭窗口
# 如果结束后要关闭窗口则用.bye()

常用方法

有空再做个明细的分类吧,反正方法不多就直接丢上来啦

.setpos(x,y)/.goto(x,y) 调整坐标

.setup(width,height,startx,starty) 初始化设置,注意x和y是指窗口左上角

.speed() 调整绘画速度

.bgcolor(color) 调整窗口背景颜色

.colormode(1.0 or 255) 1.0为RGB小数模式,255为RGB整数模式

.pencolor(color) 调整绘画颜色

.seth(theta) 调整绝对角度

.fd(d) / .bk(d) 前进后退的简写形式

.circle(r,theta)沿箭头左侧画个圆

.width(size) 调整画笔大小

.pd() / .pu() 落下画笔(有痕迹) / 放开画笔(无痕迹)

注意color有关的参数可用"white",0.1,0.2,0.3212,52,0的形式表达

这里附上一个简单应用

import turtle as tt
from random import random as rd
from random import randint as rdi

tt.setup(width=1600,height=900,startx=0,starty=0)
tt.hideturtle()
tt.pensize(30)
tt.speed(0)
for _ in range(1,1000):
    tt.pu()
    tt.goto(rdi(-800,800),rdi(-450,450))
    tt.pd()
    tt.fd(1)
    tt.pu()
    tt.pencolor(rd(),rd(),rd())
tt.done()

不只是绘图

扒了一个示例代码,感受一下吧

创建.pyw文件,运行如下代码

from random import randint
import turtle as tt

tt.TurtleScreen._RUNNING = True
tt.setup(width=800, height=450, startx=None, starty=None)  # 设置自定义的窗口大小
tt.hideturtle()  # 隐藏画笔图标
tt.color("blue")  # 画笔颜色为蓝色
tt.penup()  # 抬起画笔,移动时不画线
tt.setpos(-300, 0)  # 设置初始位置
myfont = ("黑体", 16, "normal")  # 定义字体

target = randint(1, 100)
tt.write("我想了一个1到100之间的整数,请你猜猜看吧:", font=myfont)  # 输出文本
guess = 0
answer = ""
while guess != target:
    # 使用对话框获取用户输入
    guess = tt.simpledialog.askinteger("猜数游戏", "请输入一个整数:")
    if guess == target:
        answer = "你猜对了!游戏结束。"
    elif not guess:  # 用户没有输入数字则中断循环
        tt.clear()  # 清空画布以便输出新文本
        tt.write("你放弃了,游戏结束。", font=myfont)
        break
    elif guess > target:
        answer = "你猜大了,再猜一次:"
    else:
        answer = "你猜小了,再猜一次:"
    tt.clear()
    tt.write(answer, font=myfont)
tt.done()

L系统

我也不太清楚是什么

反正挺好玩的

import turtle as tt


def generate(n, result="[X]"):
    """传入迭代次数和生成式返回结果序列
    """
    rules = {"X": "F-[[X]+X]+F[+FX]-X",
             "F": "FF"}
    for _ in range(n):
        for k, v in rules.items():
            result = result.replace(k, v)
    return result


def draw(cmds, size=2):
    """传入结果序列和线段长度绘制图形
    """
    stack = []
    for cmd in cmds:
        if cmd == "F":
            tt.forward(size)
        elif cmd == "-":
            tt.left(25)
        elif cmd == "+":
            tt.right(25)
        elif cmd == "X":
            pass
        elif cmd == "[":
            stack.append((tt.position(), tt.heading()))
        elif cmd == "]":
            position, heading = stack.pop()
            tt.penup()
            tt.setposition(position)
            tt.setheading(heading)
            tt.pendown()
        else:
            raise ValueError("Unknown Cmd: {}".format(ord(cmd)))
    tt.update()


def main():
    """绘图程序主函数
    """
    tt.TurtleScreen._RUNNING = True
    tt.hideturtle()
    tt.tracer(0)
    tt.color("green")
    tt.speed(0)
    tt.left(60)
    tt.pensize(2)
    tt.penup()
    tt.goto(-tt.window_width()/3, -tt.window_height()/3)
    tt.pendown()
    plant = generate(6)
    draw(plant)
    tt.exitonclick()


if __name__ == "__main__":
    main()

更通用的GUI

python标准库自带的tkinter能实现更通用的GUI编程

tkinker通过.tk()进行构造,运行时则启用主循环.mainloop()

import tkinter as tk
window = tk.Tk()
window.geometry("1200x800")
window.title("myTitle")
tk.mainloop()

可视化部件

常用的可视化部件有Label,Button,Entry,Text

构造方法如下

window = tk.Tk()
button = tk.Button(window,text="我是按钮")

布局时可用.pack(),.grid().place()

.pack()用例如下

label = tk.Label(window, text="测试标签")
label.pack()
button = tk.Button(window, text="测试按钮")
button.pack(side="bottom")  # 放到容器底部
entry = tk.Entry(window, width=50)  # 输入框宽50字符
entry.pack()
text = tk.Text(window, width=50, height=12, background="wheat")  # 文本区宽50字符高12字符,麦色背景
text.pack()

这种方法是默认自上而下地布局,要想一行内同时存在多列,可以使用.grid(row,column)

label = tk.Label(window, text="测试标签")
label.grid(row=0, column=0)  # 标签放在0行0列
button = tk.Button(window, text="测试按钮")
button.grid(row=0, column=1)  # 按钮放在0行1列
entry = tk.Entry(window, width=50)
entry.grid(row=1, column=0, columnspan=2, padx=20, pady=10)  
# 输入框在1行0列,横跨两列,横向留空20像素,纵向留空10像素

# 注意grid()不能与pack()混用

.place(x,y)可指定部件在容器中的绝对坐标(以左上角为原点)

text = tk.Text(window, width=50, height=12, background="wheat")
text.place(x=20, y=100)  # 文本区放在指定的坐标

交互

挺简单的我直接搬用例吧

import tkinter as tk


def change(widget, var):
    """事件处理函数:改变Text部件的文本
    在widget现有文本末尾插入新的文本var
    """
    widget.config(state="normal")
    widget.insert("end", var + "\n")
    widget.config(state="disabled")


def main():
    """主函数:设置窗口部件,指定按钮点击事件处理函数
    """
    window = tk.Tk()
    window.geometry("500x500")
    window.title("简单图形界面程序")
    label = tk.Label(window, text="请输入文本并点击添加")
    label.grid(row=0, column=0)
    entry = tk.Entry(window, width=50)
    entry.grid(row=1, column=0, columnspan=2, padx=20, pady=10)
    text = tk.Text(window, width=50, height=12, background="wheat")
    text.config(state="disabled") # 禁止输入
    text.place(x=20, y=100)
    button = tk.Button(window, text="添加",
                       command=lambda: change(text, entry.get()))
    button.grid(row=0, column=1)
    tk.mainloop()


if __name__ == "__main__":
    main()
"""tkimage.pyw 简单的图片查看器
"""
import tkinter as tk
import tkinter.filedialog as fd # 文件处理


def openimage(canvas):
    """事件处理函数:使用文件对话框打开图片
    """
    filename = fd.askopenfilename(filetypes=[("PNG图片", "*.png"),
                                             ("GIF图片", "*.gif")])
    global image  # 注意这个需要定义为全局变量
    image = tk.PhotoImage(file=filename)
    # 第三方包pillow可以打开更多的图片格式
    canvas.create_image((0, 0), image=image, anchor="nw")


def main():
    """主函数:设置窗口部件,指定按钮点击事件处理函数
    """
    window = tk.Tk()
    window.geometry("600x480")
    window.title("简单的图片查看器")
    canvas = tk.Canvas(window, width=600, height=440) #一个新的部件
    canvas.pack(side="bottom")
    button = tk.Button(window, text="打开图片",
                       command=lambda: openimage(canvas))
    button.pack()
    tk.mainloop()


if __name__ == "__main__":
    main()

# author: starglow_leo
"""tkcalc.pyw 简单的计算器
"""
import tkinter as tk


class Calc(tk.Tk):
    """计算器窗体类"""
    def __init__(self):
        """初始化实例"""
        tk.Tk.__init__(self)
        self.title("计算器")
        self.memory = 0  # 暂存数值
        self.create()

    def create(self):
        """创建界面"""
        btn_list = ["C", "M->", "->M", "/",
                    "7", "8", "9", "*",
                    "4", "5", "6", "-",
                    "1", "2", "3", "+",
                    "+/-", "0", ".", "="]
        r = 1
        c = 0
        for b in btn_list:
            self.button = tk.Button(self, text=b, width=5,
                                    command=(lambda x=b: self.click(x)))
            self.button.grid(row=r, column=c, padx=3, pady=6)
            c += 1
            if c > 3:
                c = 0
                r += 1
        self.entry = tk.Entry(self, width=24, borderwidth=2,
                              bg="yellow", font=("Consolas", 12))
        self.entry.grid(row=0, column=0, columnspan=4, padx=8, pady=6)

    def click(self, key):
        """响应按钮"""
        if key == "=":  # 输出结果
            result = eval(self.entry.get())
            self.entry.insert(tk.END, " = " + str(result))
        elif key == "C":  # 清空输入框
            self.entry.delete(0, tk.END)
        elif key == "->M":  # 存入数值
            self.memory = self.entry.get()
            if "=" in self.memory:
                ix = self.memory.find("=")
                self.memory = self.memory[ix + 2:]
            self.title("M=" + self.memory)
        elif key == "M->":  # 取出数值
            if self.memory:
                self.entry.insert(tk.END, self.memory)
        elif key == "+/-":  # 正负翻转
            if "=" in self.entry.get():
                self.entry.delete(0, tk.END)
            elif self.entry.get()[0] == "-":
                self.entry.delete(0)
            else:
                self.entry.insert(0, "-")
        else:  # 其他键
            if "=" in self.entry.get():
                self.entry.delete(0, tk.END)
            self.entry.insert(tk.END, key)


if __name__ == "__main__":
    Calc().mainloop()
    
# author: starglow_leo

模式匹配

光是学会简单的哈希和KMP是不够用的,所以还是记一下正则表达式的用法

在python中可用标准库提供的re模块

用法如下

import re
s = "1881001118835400188101888992360018801392999236001881010005523600"
lis = re.findall(r"1\d{10}",s) # 1后接任意10个数字
print(lis)
# ['18810011188', '18810188899', '18801392999', '18810100055']

正则转义码

代码转义说明
d数字类字符,默认也包括全角数字
D非数字类字符
w单词类字符,默认也包括汉字等
W非单词类字符
s空白类字符,即空格/制表/换行等
S非空白类字符
b单词边界,用于精确匹配单词
B非单词边界

特殊功能符号

符号功能正则示例目标示例
.匹配除n任意字符a.cabc acc
\转义a.ca.c
*匹配前一字符任意次(包括0)ab*a ab abbb
+匹配前一字符任意次(不包括0)ab*ab abbb
?匹配前一字符0或1次ab?a ab
{lo,hi}匹配前一字符[lo,hi]次,{lo}表示[lo,inf]ab{1,2}cabc abbc
^匹配字符串开头^abab
$匹配字符串结尾ab$ab
\ ab\bcab bc
[]指定字符集a[bc]eabe ace
()子表达式分组(ab){2}abab ababab

其中[a-z]指定a到z任意小写字母,[ ^abc]表示除了abc以外的任意字符

常用方法

.compile() 编译正则表达式,返回模式对象,在多次复用同一正则表达式中能提高效率

.findall() 查找所有匹配文本,返回字符串列表

.split() 用匹配文本拆分字符串并返回字符串列表

.sub() 按规则替换文本并返回新的字符串

.subn() 按规则替换文本并返回替换次数

.match() 从字符串开头匹配文本,返回匹配对象

.search() 在字符串内查找匹配文本,返回匹配对象

.finditer() 在字符串内查找所有匹配文本,返回匹配对象迭代器

以下是用于匹配对象的方法

.group() 返回匹配的字符串

.start() 返回匹配开始的位置

.end() 返回匹配结束的位置

.span() 返回匹配开始和结束的位置(元组)

.groups() 返回匹配的所有分组字符串(元组)

示例

import re
import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')

p = re.compile(r"\w+")
s = "不盈一握之迷惘,由此孕育出邪念,爬过旷野,翻越山丘,播撒灾厄于长空"
g = re.findall(p,s)
for i in g:
    print(i)
# 不盈一握之迷惘
# 由此孕育出邪念
# 爬过旷野
# 翻越山丘
# 播撒灾厄于长空

# 等同于
# g = re.finditer(p,s)
# for i in g:
#     print(i.group())

当有多个分组时,.group(idx)idx可指定第几组,其中0(缺省值)代表全部

import re
s = "xjb: 1wk2cd4dk"
p = re.compile(r"(\d)(\D)")
g = p.search(s,pos=6)
print(g.group(0)) # 2c
print(g.group(1)) # 2

迭代机制

基本方法

在python中判断对象是否为可迭代的依据是内部成员有无__iter__

s = "iterator"
print(hasattr(s,"__iter__")) # 返回True

要想得到迭代器,则使用iter(obj)

i = iter(s)
print(type(i)) # <class 'str_iterator'>
print(i) # <str_iterator object at 0x0194E0B0>

要想得到迭代器的下一个元素,则用next(it)

print(next(i)) # i
print(next(i)) # t
# 当到达最后一个元素时
print(next(i)) # 抛出StopIteration异常

需要注意迭代过程中in的细节,当找出第一个满足的元素则结束,否则会全部遍历

it = iter("iterator")
print("i" in it) # True
next(it)
print("i" in it) # False
next(it) # 抛出StopIteration异常

自定义

要实现可迭代的类,就需要定义好__iter____next__

这里搬一个用例,逐个输出$2^n$

class Power2n:
    """2的正整数次幂数列迭代器类
    """
    def __init__(self, n):
        self.n = n  # 数列长度
        self.cur = 1  # 当前幂次

    def __iter__(self):  # 可迭代对象必须实现__iter__方法来返回迭代器
        return self

    def __next__(self):  # 迭代器必须实现__next__方法来返回下一个元素
        if self.n >= self.cur:
            result = 2 ** self.cur
            self.cur += 1
            return result
        else:  # 没有元素可返回则抛出停止迭代异常
            raise StopIteration()

python还提供更简便的生成器函数,可自动迭代和处理迭代异常

如下

def Power2nX(n):
    """2的正整数次幂数列生成器函数
    """
    for i in range(1, n + 1):
        yield 2 ** i # yield返回一个生成器对象

再简便点的就是解析式语法,前面已经有过示例了

时间操作

简单的计数

time模块可实现简单的时间操作

其中.time()返回一个时间戳(浮点,指代距1970/1/1的秒数),

.sleep(sec)会让程序暂停指定秒数

.perf_counter()返回运行的总时间

.process_time()返回进程实际运行时间(不计sleep)

日期计算

datetime模块中.datetime.fromtimestamp(ts)可以将时间戳转换为当地时的datetime对象,.datetime.utcfromtimestamp(ts)则是utc+0

以我写这份代码的时间为例

import time
import datetime as dt
t = time.time()
print(dt.datetime.fromtimestamp(t))
# 2019-01-20 14:20:13.327391
print(dt.datetime.utcfromtimestamp(t))
# 2019-01-20 06:20:13.327391
print(type(dt.datetime.fromtimestamp(t)))
# <class 'datetime.datetime'>

datetime对象包含int类型的year,month,day,hour,minute,secondmicrosecond

构造时可以依这些参数进行构造

import time
import datetime as dt
d = dt.datetime(year=1970,month=1,day=1)
s = str(d)
print(s) # 1970-01-01 00:00:00

也可以直接用.datetime.now().datetime.utcnow()获取当前时间对应的datetime对象

import time
import datetime as dt
d = dt.datetime.now()
print(d-dt.timedelta(days=365))
# 2018-01-20 14:32:36.833662

代码中出现的timedelta类可用weeks,days,hours,minutes,seconds,millisecondsmicroseconds进行构造,没有年月的不固定长度

关于时区的管理可用timezone类,datetime对象需要加上timezone类型的tzinfo才能准确定位时间点

import time
import datetime as dt
d = dt.datetime(1970,1,1,tzinfo=dt.timezone.utc)
print(d) # 1970-01-01 00:00:00+00:00

更详细的用法

import time
import datetime as dt
d = dt.datetime.now()
print(d) # 2019-01-20 14:41:02.902480
d = d.replace(tzinfo=dt.timezone(dt.timedelta(seconds=-time.timezone)))
print(d) # 2019-01-20 14:41:02.902480+08:00
print(d.astimezone(dt.timezone(dt.timedelta(hours=9))))
# 2019-01-20 15:41:02.902480+09:00

转换字符串可用datetime实例中的strftime()方法返回指定格式的日期字符串

反之,用strptime()可将日期字符串转换为datetime对象

import time
import datetime as dt
import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
d = dt.datetime.now()
s = d.strftime("%Y年%m月%d日%H时%M分%S秒".encode("unicode-escape").decode()).encode().decode("unicode-escape")
print(s)
import time
import datetime as dt
d = dt.datetime.strptime("1970年1月1日0时", "%Y年%m月%d日%H时")
print(d) # 1970-01-01 00:00:00

异常

python使用try-except-else语句来抛出异常

b=int(input())
try:
    a=3/b
except ZeroDivisionError:
    print("ERROR")
else:
    print(a)

如果希望异常时不做提示,可以使用pass

try:
    a=3/0
except:
    pass
else:
    print(a)

无论失败与否都要运行,则可用finally

自定义异常

所有的异常来自BaseException,一般自定义继承自Exception(其父节点是BaseException)

class WrongAnswer(Exception):
    def __init__(self):
        print('WA')

raise WrongAnswer()

任务调度

暂无

标准库

查阅库可用方法help(),dir()

这里列出比较实用的(目录)

os提供与操作系统相关的方法,如.getcwd(),.system(),.chdir()

shutil提供任务和目录管理相关方法,.copyfile(),.move()

glob提供一个函数从目录通配符搜索中生成文件列表.glob()

sys中的.argv保存从命令行中输入的参数,还有.stdin,.exit()等成员

re提供正则表达式相关工具

math提供数学运算的支持

random可用于生成随机数,如.sample(range,num),.choice(list),random(),randrange(num)

urllib.request处理url接收的数据

smtplib处理电子邮箱协议

datetime提供日期相关的运算和格式化工具

zlib处理压缩文件

timeit.Timer可用Timer(fun).timeit()测量性能

unittest,doctest用于开发测试

杂项

字符串强转数字

inp = "2221222122231"
print(inp)
lis = inp.split("1")
num = []
for elem in lis:
    try:
        num.append(int(elem)) #强行忽略错误的空串
    except:
        pass
print(num)

列表格式强转列表

inp = "[12, 34, 56, 78]\n"
lis = inp.lstrip("[").rstrip("]\n").split(", ")
print(lis)
nums = [int(chars) for chars in lis]
print(nums)

递归示例new

class ListNode:
    def __init__(self,val):
        self.next=None
        self.val=val
    def print(self):
        print(self.val,end='\n' if self.next==None else ' ')
        if(self.next!=None):
            self.next.print()
l1 = ListNode(1)
l2 = l1
for i in range(2,5):
    l2.next=ListNode(i)
    l2=l2.next
l2.print()
l1.print()

附录

第三方包

第三方包查询

pip配置

有些Python包的体积很大,从位于境外的官方源下载需要较长时间。你可以设置从中国大陆的镜像源(例如阿里云)下载软件包,这样速度会快上许多。如果你用的操作系统是Win7-10,请在资源管理器地址栏输入C:ProgramData打开这个隐藏目录,在其中创建pip文件夹,再在其中创建pip.ini文件并复制粘贴以下内容,即配置好了镜像源:

[global]
index-url = https://mirrors.aliyun.com/pypi/simple/

[list]
format = columns

python编程规范

详见PEP8

(你以为我会乖乖听你的?)

python资源大全

awesome-python

last update:2019/01/29

标签: none

添加新评论