Python学习教程

认识Python

本文主要是针对Python3的学习教程。

Python简介

Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言。
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。
Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。

  • Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节。类似于PHP和Perl语言。
  • Python 是交互式语言: 这意味着,您可以在一个 Python 提示符 >>> 后直接执行代码。
  • Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。
  • Python 是初学者的语言:Python 对初级程序员而言,是一种伟大的语言,它支持广泛的应用程序开发,从简单的文字处理到 WWW 浏览器再到游戏。

Python 特点

  • 易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。
  • 易于阅读:Python代码定义的更清晰。
  • 易于维护:Python的成功在于它的源代码是相当容易维护的。
  • 一个广泛的标准库:Python的最大的优势之一是丰富的库,跨平台的,在UNIX,Windows和Macintosh兼容很好。
  • 互动模式:互动模式的支持,您可以从终端输入执行代码并获得结果的语言,互动的测试和调试代码片断。
  • 可移植:基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。
  • 可扩展:如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。
  • 数据库:Python提供所有主要的商业数据库的接口。
  • GUI编程:Python支持GUI可以创建和移植到许多系统调用。
  • 可嵌入: 你可以将Python嵌入到C/C++程序,让你的程序的用户获得”脚本化”的能力。

Python就为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作“内置电池(batteries included)”。用Python开发,许多功能不必从零编写,直接使用现成的即可。

除了内置的库外,Python还有大量的第三方库,也就是别人开发的,供你直接使用的东西。当然,如果你开发的代码通过很好的封装,也可以作为第三方库给别人使用。

许多大型网站就是用Python开发的,例如YouTube、Instagram,还有国内的豆瓣。很多大公司,包括Google、Yahoo等,甚至NASA(美国航空航天局)都大量地使用Python。

龟叔给Python的定位是“优雅”、“明确”、“简单”,所以Python程序看上去总是简单易懂,初学者学Python,不但入门容易,而且将来深入下去,可以编写那些非常非常复杂的程序。

总的来说,Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。如果一个资深程序员向你炫耀他写的晦涩难懂、动不动就几万行的代码,你可以尽情地嘲笑他。

Python适合开发的应用

  • 网络应用,包括网站、后台服务等等

  • 日常需要的小工具,包括系统管理员需要的脚本任务等等

  • 把其他语言开发的程序再包装起来,方便使用

Python的缺点

  • 运行速度慢。 和C程序相比非常慢,因为Python是解释型语言,你的代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。而C程序是运行前直接编译成CPU能执行的机器码,所以非常快。
  • 代码不能加密。 如果要发布你的Python程序,实际上就是发布源代码,这一点跟C语言不同,C语言不用发布源代码,只需要把编译后的机器码(也就是你在Windows上常见的xxx.exe文件)发布出去。要从机器码反推出C代码是不可能的,所以,凡是编译型的语言,都没有这个问题,而解释型的语言,则必须把源码发布出去。

安装Python

Windows安装Python3

下载Windows版Python

官网地址: https://www.python.org/

点击Downloads

安装运行python msi文件

最后 点击 Install 进行安装

Linux安装Python3

下载Linux版python

到官网下载,打开方式和上述的Windows一样,在文件选择的地方选择Source release

Gizp或者XZ都可

我这里是下载的gzip

1
2
3
4
5
6
7
8
[root@master ~]# wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz
--2019-02-25 10:58:14-- https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz
Connecting to 127.0.0.1:8118... connected.
Proxy request sent, awaiting response... 200 OK
Length: 22897802 (22M) [application/octet-stream]
Saving to: ?𻱐ython-3.7.2.tgz?

100%[============================================================================================>] 22,897,802 435KB/s in 55s

安装Linux版本Python

安装依赖包

yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel

解压源码包

1
2
[root@master ~]# tar zxf Python-3.7.2.tgz 
[root@master ~]#

编译源码包

**安装到/usr/loal/python3目录

1
2
3
4
[root@master ~]# cd Python-3.7.2/
[root@master Python-3.7.2]# ./configure --prefix=/usr/local/python3
[root@master Python-3.7.2]# make
[root@master Python-3.7.2]# make install

遇到安装问题:

  • ModuleNotFoundError: No module named ‘_ctypes’
    解决方法
    yum install libffi-devel -y

    Hello World 第一个Python程序

    交互模式

    1
    2
    3
    4
    5
    6
    7
    8
    [root@master bin]# /usr/local/python3/bin/python3
    Python 3.7.2 (default, Feb 25 2019, 11:21:38)
    [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print("Hello World!")
    Hello World!
    >>> exit()
    [root@master bin]#

直接运行py文件

[root@master ~]# vim hello.py
内容如下

1
2
3
#!/usr/local/python3/bin/python3

print("Hello World")

为文件增加可执行权限

1
2
[root@master ~]# chmod +x hello.py 
[root@master ~]#

执行脚本运行

1
2
3
[root@master ~]# ./hello.py 
Hello World
[root@master ~]#

直接运行.py文件相当于启动了Python解释器,然后一次性把.py文件的源代码给执行了。

Python3 默认的字符集是utf-8
hello.py 增加 “你好,世界” 中文字符

1
2
3
4
5
[root@master ~]# cat hello.py 
#!/usr/local/python3/bin/python3

print("Hello World")
print("你好,世界")

再次执行hello.py

1
2
3
[root@master ~]# ./hello.py 
Hello World
你好,世界

Python2 中文解决方法为只要在文件开头加入**# -\- coding: UTF-8 -*-或者 #coding=utf-8* 就行了

Python 语法规范

Python 标识符

标识符由字母、数字、下划线组成。

所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。

标识符是区分大小写

以下划线开头的标识符是有特殊意义的

  • 以单下划线开头 _foo 的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 from xxx import * 导入
  • 以双下划线开头的 __foo 代表类的私有成员,以双下划线开头和结尾的__foo__ 代表 Python 里特殊方法专用的标识,如__init__() 代表类的构造函数

Python 可以同一行显示多条语句,方法是用分号 ; 分开

1
2
3
>>> print("Hello");print("GooD")
Hello
GooD

Python 保留字符

Python的保留字符不能用作常量或者变量

andexecnot
assertfinallyor
breakforpass
classfromprint
continueglobalraise
defifreturn
delimporttry
elifinwhile
elseiswith
exceptlambdayield

Python书写行的缩进

python 用缩进来写模块。缩进的空白数量是可变的,但是所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行。

例1

1
2
3
4
5
>>> if 1==2:
... print("True")
... else:
... print("False")
...

上述结果返回“False”

例2

1
2
3
4
5
6
7
8
[root@master ~]# cat i.py 
#!/usr/local/python3/bin/python3
a=0
if a!=0:
print("不是零")
else:
print("是零")
print("Here")

运行报错如下:

1
2
3
4
File "./i.py", line 7
print("Here")
^
IndentationError: unindent does not match any outer indentation level

Python 多行语句

Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠()来实现多行语句, 如下:

1
2
3
total = "one" + \
"two" + \
"three"

例1

1
2
3
4
5
>>> total = "one" + \
"two" + \
"three"... ...
>>> print(total)
onetwothree

在 [], {}, 或 () 中的多行语句,不需要使用反斜杠()

例2

1
2
3
4
>>> total = ['one', 'two', 'three',
'four', 'five']...
>>> print(total)
['one', 'two', 'three', 'four', 'five']

空行

空行与代码缩进不同,空行并不是Python语法的一部分。

书写时不插入空行,Python解释器运行也不会出错。但是空行的作用在于分隔两段不同功能或含义的代码,便于日后代码的维护或重构。

空行也是程序代码的一部分。

  • 函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。
  • 类和函数入口之间也用一行空行分隔,以突出函数入口的开始。

多个语句构成代码组

缩进相同的一组语句构成一个代码块,我们称之代码组。

像if、while、def和class这样的复合语句,首行以关键字开始,以冒号( : )结束,该行之后的一行或多行代码构成代码组。

1
2
3
4
5
6
7
>>> a = 1
>>> if a == 1:
... print(a+1)
... elif a > 0:
... print(a+2)
... else:
... print(a+3)

Python 引号

Python 可以使用引号( ‘ )、双引号( “ )、三引号( ‘’’ 或 “”” ) 来表示字符串,引号的开始与结束必须的相同类型的。

其中三引号可以由多行组成,编写多行文本的快捷语法,常用于文档字符串,在文件的特定地点,被当做注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> a = '1'
>>> b = "2"
>>> c = """1
... 2
... 3
... """
>>> print(a)
1
>>> print(b)
2
>>> print(c)
1
2
3

Python注释

python中单行注释采用 # 开头。

1
2
3
4
5
#!/usr/local/python3/bin/python3
# 第一个注释
print("Hello, World!") # 第二个注释
输出结果:
Hello, World!

注释可以在语句或表达式行末:

name = "farmer" # 设置name变量为farmer【注释前面代码含义】

python 中多行注释使用三个单引号(‘’’)或三个双引号(“””)。

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/local/python3/bin/python3
'''
这是多行注释,使用单引号。
这是多行注释,使用单引号。
这是多行注释,使用单引号。
'''

"""
这是多行注释,使用双引号。
这是多行注释,使用双引号。
这是多行注释,使用双引号。
"""

Python 的数值变量类型

Python的变量存在内存中,所以在创建变量的时候会在内存中开辟一个内存空间给变量。
变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符串

变量的赋值

等号(=)用来给变量赋值。
等号(=)运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值。

例子

1
2
3
4
5
6
7
8
9
>>> a = 1 # 整型
>>> b = 1.2 # 浮点型
>>> c = "farmer" # 字符串
>>> print(a, type(a))
1 <class 'int'>
>>> print(b, type(b))
1.2 <class 'float'>
>>> print(c, type(c))
farmer <class 'str'>

多个变量赋值

Python允许你同时为多个变量赋值。例如:
a = b = c = 1
以上实例,创建一个整型对象,值为1,三个变量被分配到相同的内存空间上。
您也可以为多个对象指定多个变量。例如:
a, b, c = 1, 2, "farmer"
以上实例,两个整型对象 1 和 2 分别分配给变量 a 和 b,字符串对象 “farmer” 分配给变量 c.

标准数据类型

Python有五个标准的数据类型:

  • Numbers(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Dictionary(字典)
  • Sets(集合)

Python 数字类型

Python3有四种不同的数字类型:

  • int(有符号整型)
  • bool(布尔)
  • float(浮点型)
  • complex(复数)

数字类型用于存储数值,当你指定一个数值的时候Number对象就会被创建,也可以用del对创建的对象进行删除。

在Python 3里,只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
像大多数语言一样,数值类型的赋值和计算都是很直观的。
内置的type()函数可以用来查询变量所指的对象类型。

1
2
3
>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

整型

赋值

1
2
3
>>> x = 1      
>>> print(type(x))
<class 'int'>

删除

1
2
3
4
5
>>> del x
>>> print(type(x))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

bool

在Python2中是没有布尔型的,它用数字0表示False,用1表示True。到Python3中,把True和False定义成关键字了,但它们的值还是1和0,它们可以和数字相加。

1
2
3
4
>>> print(True+1)
2
>>> print(False+1)
1

数值运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 5 + 4  # 加法
9
>>> 4.3 - 2 # 减法
2.3
>>> 3 * 7 # 乘法
21
>>> 2 / 4 # 除法,得到一个浮点数
0.5
>>> 2 // 4 # 除法,得到一个整数
0
>>> 17 % 3 # 取余
2
>>> 2 ** 5 # 乘方
32
  • Python可以同时为多个变量赋值,如a, b = 1, 2。
  • 一个变量可以通过赋值指向不同类型的对象。
  • 数值的除法(/)总是返回一个浮点数,要获取整数使用//操作符。
  • 在混合计算时,Python会把整型转换成为浮点数。

String字符串

Python中的字符串用单引号(‘)或双引号(“)括起来,同时使用反斜杠()转义特殊字符

字符串的截取的语法格式如下:

  • 变量[头下标:尾下标]
  • 索引值以 0 为开始值,-1 为从末尾的开始位置。
  • 加号 (+) 是字符串的连接符, 星号 (*) 表示复制当前字符串,紧跟的数字为复制的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> str = "farmer"
>>> print (str) # 输出字符串
farmer
>>> print (str[0:-1]) # 输出第一个到倒数第二个的所有字符
farme
>>> print (str[0]) # 输出字符串第一个字符
f
>>> print (str[2:5]) # 输出从第三个开始到第五个的字符
rme
>>> print (str[2:]) # 输出从第三个开始的后的所有字符
rmer
>>> print (str * 2) # 输出字符串两次
farmerfarmer
>>> print (str + "TEST") # 连接字符串
farmerTEST
>>>

Python 使用反斜杠(\)转义特殊字符,如果你不想让反斜杠发生转义,可以在字符串前面添加一个 r,表示原始字符串:

1
2
3
4
5
>>> print("far\nmer")
far
mer
>>> print(r"far\nmer")
far\nmer

str.capitalize

返回首字母大写,其他字母小写的字符串(str指一个字符串)

1
2
3
>>> str="my name is farmer"
>>> str.capitalize()
'My name is farmer'

str.casefold

字符串转换成小写,用于不区分大小写的字符串比较(str指一个字符串)

1
2
3
>>> str="My Name Is Farmer"
>>> str.casefold()
'my name is farmer'

str.center

返回指定长度的字符串,字符串内容居中,并使用指定字符填充(str指一个字符串)

1
2
3
>>> str="Hello World"
>>> str.center(20, "*")
'****Hello World*****'

str.count

返回子字符串在字符串中出现的次数(区分大小写)

1
2
3
4
5
>>> str="Hello My Dear Mum"
>>> str.count("M")
2
>>> str.count("m")
1

str.expandtabs

使用空格替换tab

1
2
3
>>> str="This\tMy\tHome"
>>> str.expandtabs()
'This My Home'

str.endswith

判断字符串是否以指定的后缀结尾

1
2
3
4
5
>>> str="Farmer"
>>> str.endswith('r')
True
>>> str.endswith('F')
False

str.startswith

判断字符串是否以指定的后缀开头

1
2
3
4
5
>>> str="Farmer"
>>> str.startswith('F')
True
>>> str.startswith('r')
False

str.find

返回子字符串在字符串中第一次出现的位置;如没找到,返回-1

1
2
3
4
5
6
7
>>> str="Farmer"
>>> str.find('r')
2
>>> str.find('r', 3) # 从下标3开始查找
5
>>> str.find('F', 3)
-1

str.format

执行字符串格式化操作,替换字段使用{}分隔,替换字段可以是表示位置的位置或keyword参数名字

1
2
3
>>> str="Hello {0}, Welcome {1}"
>>> str.format("Farmer", "My Home")
'Hello Farmer, Welcome My Home'
1
2
3
>>> str = "Hello {name}, Welcome {address}"
>>> str.format(name="Farmer", address="BeiJing")
'Hello Farmer, Welcome BeiJing'

str.format_map

执行字符串格式化操作,替换字段使用{}分隔,同str.for

1
2
3
4
5
6
>>> str = "Hello {name}, Welcome {address}"
>>> name="Farmer"
>>> address="BJ"
>>> str.format_map(vars())
'Hello Farmer, Welcome BJ'
>>>

str.isalnum

判断字符串中是否至少有一个字符,并且所有字符都是字母或数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> str="123456789"
>>> str.isalnum()
True
>>> str="abcef"
>>> str.isalnum()
True
>>> str="123abc"
>>> str.isalnum()
True
>>> str=''
>>> str.isalnum()
False
>>> str="abc 123"
>>> str.isalnum()
False
>>>

str.isalpha

判断字符串中是否至少有一个字符,并且所有字符都是字母

1
2
3
4
5
6
7
8
9
10
>>> str="abc"
>>> str.isalpha()
True
>>> str="abc123"
>>> str.isalpha()
False
>>> str="a bc"
>>> str.isalpha()
False
>>>

str.isdecimal

判断字符串中是否至少有一个字符,并且所有字符都是十进制数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> x = "1"
>>> x.isdecimal()
True
>>> x = ''
>>> x.isdecimal()
False
>>> x = "hello"
>>> x.isdecimal()
False
>>> x="1.1"
>>> x.isdecimal()
False
>>> x = "1a"
>>> x.isdecimal()
False

str.isdigit

判断字符串中是否至少有一个字符,并且所有字符都是数字

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x="1"
>>> x.isdigit()
True
>>> x="1a"
>>> x.isdigit()
False
>>> x=""
>>> x.isdigit()
False
>>> x='四'
>>> x.isdigit()
False
>>>

str.isnumeric

判断字符串中是否至少有一个字符,并且所有字符都是数字字符

1
2
3
4
5
6
7
8
9
>>> num='四'
>>> num.isnumeric()
True
>>> num="1"
>>> num.isnumeric()
True
>>> num="abc123"
>>> num.isnumeric()
False

str.islower

判断字符串中是否小写并且至少有一个字符

1
2
3
4
5
6
>>> str="Farmer"
>>> str.islower()
False
>>> str="farmer"
>>> str.islower()
True

str.isprintable

判断字符串的所有字符都是可打印字符或字符串为空

1
2
3
4
5
6
7
8
9
10
>>> s="\n"
>>> s.isprintable()
False
>>> s="farmer"
>>> s.isprintable()
True
>>> s=''
>>> s.isprintable()
True
>>>

str.isspace

判断字符串中是否至少有一个字符,并且所有字符都是空白字符

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> s=''
>>> s.isprintable()
True
>>>
>>> s=''
>>> s.isspace()
False
>>> s=' '
>>> s.isspace()
True
>>> s='a '
>>> s.isspace()
False

str.isupper

判断字符串中是否大字并且至少有一个字符

1
2
3
4
5
6
>>> str="Farmer"
>>> str.isupper()
False
>>> str="FARMER"
>>> str.isupper()
True

str.lower()

将字符串中的大小转换为小写

1
2
3
>>> str="FARMER"
>>> str.lower()
'farmer'

str.upper()

将字符串的小写转换为大写

1
2
3
>>> str="farmer"
>>> str.upper()
'FARMER'

str.join

使用字符串作为分隔符串连多个数据为一个字符串

1
2
3
4
5
6
>>> str="farmer"
>>> "|".join(str)
'f|a|r|m|e|r'
>>> "-".join(str)
'f-a-r-m-e-r'
>>>

str.ljust

返回指定长度的字符串,字符串内容居左,并使用指定字符填充

1
2
3
4
>>> str="farmer"
>>> str.ljust(8, '@')
'farmer@@'
>>>

str.rjust

返回指定长度的字符串,字符串内容居右,并使用指定字符填充

1
2
3
4
>>> str='farmer'
>>> str.rjust(8, '#')
'##farmer'
>>>

str.lstrip

用于去掉字符串左边的空格或指定字符

1
2
3
4
5
6
7
>>> str=" farmer "
>>> str.lstrip()
'farmer '
>>> str = "aaaafarmeraaaa"
>>> str.lstrip("a")
'farmeraaaa'
>>>

str.rstrip

去掉字符串后面的空格,或参数中的字符

1
2
3
4
5
6
7
>>> str=' farmer '
>>> str.rstrip()
' farmer'
>>> str = "aaaafarmeraaaa"
>>> str.rstrip('a')
'aaaafarmer'
>>>

str.strip

去掉字符串前后的空格,或指定的所有字符

1
2
3
4
>>> str=' farmer '
>>> str.strip()
'farmer'
>>>

str.partition

返回匹配第一个字符串中分隔符之前、分隔符、分隔符之后的子字符串的tuple

1
2
3
4
>>> str='farmer'
>>> str.partition('r')
('fa', 'r', 'mer')
>>>

str.rpartition

从后往前查找,返回包含字符串中分隔符之前、分隔符、分隔符之后

1
2
3
>>> str.rpartition('r')
('farme', 'r', '')
>>>

str.replace

替换字符串中所有的旧子字符串为新的字符串

1
2
3
4
>>> str="farmer"
>>> str.replace('r', 'R')
'faRmeR'
>>>

str.rfind

返回子字符串在字符串中最后一次出现的位置;如没找到,返回-1

1
2
3
4
>>> str='farmer'
>>> str.rfind('r')
5
>>>

str.split

拆分字符串,返回一个列表

1
2
3
4
>>> str='farmer'
>>> str.split('r')
['fa', 'me', '']
>>>

str.splitlines

字符串以换行符为分隔符拆分,去掉换行符

1
2
3
4
>>> str='far\nm\ter'
>>> str.splitlines()
['far', 'm\ter']
>>>

str.zfill

在字符串的左边填充0,不会截断字符串

1
2
3
4
>>> str='farmer'
>>> str.zfill(8)
'00farmer'
>>>

List(列表)

List(列表) 是 Python 中使用最频繁的数据类型。
列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)。
列表是写在方括号([])之间、用逗号分隔开的元素列表。
和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。
列表截取的语法格式如下:
变量[头下标:尾下标]
索引值以 0 为开始值,-1 为从末尾的开始位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> m = ['a', 'b', 1, 2, 'c', 4]
>>> print(m)
['a', 'b', 1, 2, 'c', 4]
>>> m[0] # 列表第一个索引是0,打印第一个索引
'a'
>>> m[1:3] # 获取列表索引1到2的值(不包含3)
['b', 1]
>>> m[4:] # 获取从索引4开始以后的所有的值
['c', 4]
>>> m[-1] # 获取列表最后一个索引的值
4
>>> m[-2] # 获取列表的倒数第二个索引的值
'c'
>>>

list.append

增加一个对象到列表内

1
2
3
4
5
6
7
>>> m = ['a', 'b']
>>> print(m)
['a', 'b']
>>> m.append('c')
>>> print(m)
['a', 'b', 'c']
>>>

list.clear

清空列表

1
2
3
4
5
6
>>> print(m)
['a', 'b', 'c']
>>> m.clear()
>>> print(m)
[]
>>>

list.copy

复制列表,浅copy

1
2
3
4
5
6
7
8
9
10
11
>>> m = ['age', 'name', 'home']
>>> print(m)
['age', 'name', 'home']
>>> n = m.copy()
>>> print(n)
['age', 'name', 'home']
>>> print(id(m)) # m的id
140195260501960
>>> print(id(n)) # n的id
140195259746376
>>>

深copy和浅copy的区别

Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块。

  • copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
  • copy.deepcopy 深拷贝 拷贝对象及其子对象

参考例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/local/python3/bin/python3
import copy
a = [1, 2, 3, 4, ['a', 'b']]

b = a # 赋值
c = copy.copy(a) # 浅拷贝
d = copy.deepcopy(a) # 深拷贝

a.append(5) # 修改对象a的值
a[4].append('c') # 修改a中的列表['a', 'b']的值

print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d)

运行结果

1
2
3
4
5
[root@master ~]# ./c.py 
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']]

list.count

统计列表中某一元素在列表中的次数

1
2
3
4
5
6
>>> m = ['a', 'b', 'c', 'c', 'a', 'A']
>>> m.count('a')
2
>>> m.count('A')
1
>>>

list.extend

列表增加列表内容

1
2
3
4
5
6
>>> m = ['a', 'b', 'c']
>>> n = [1, 2, 3]
>>> m.extend(n)
>>> m
['a', 'b', 'c', 1, 2, 3]
>>>

list.index

返回列表元素在列表中第一次出现的位置

1
2
3
4
>>> m = ['a', 'b', 'a']
>>> m.index('a')
0
>>>

list.insert

在列表指定位置插入数值

1
2
3
4
5
>>> m = ['a', 'b', 'a']
>>> m.insert(1, 'c')
>>> m
['a', 'c', 'b', 'a']
>>>

list.pop

默认删除列表尾部元素,可指定索引删除

1
2
3
4
5
6
7
8
9
10
>>> m = [1, 2, 3, 4, 5, 6, 1, 2 ,3]
>>> m.pop()
3
>>> m
[1, 2, 3, 4, 5, 6, 1, 2]
>>> m.pop(0)
1
>>> m
[2, 3, 4, 5, 6, 1, 2]
>>>

list.remove

删除指定列表元素的第一个匹配项

1
2
3
4
>>> m = ['a', 'b', 'c', 'a', 'b']
>>> m.remove('a')
>>> m
['b', 'c', 'a', 'b']

list.reverse

列表元素反转

1
2
3
4
5
6
7
>>> m = [1, 2, 3, 4, 5]
>>> m
[1, 2, 3, 4, 5]
>>> m.reverse()
>>> m
[5, 4, 3, 2, 1]
>>>

list.sort

根据AscII码大小对列表元素排序

1
2
3
4
5
6
7
8
>>> m = ['a', 'b', 'd', 'c']
>>> m.sort()
>>> m
['a', 'b', 'c', 'd']
>>> m = ['a', '1', 'c', '3', 'b', '2']
>>> m.sort()
>>> m
['1', '2', '3', 'a', 'b', 'c']

Tuple(元组)

元组(tuple)与列表类似,不同之处在于元组的元素不能修改。元组写在小括号(())里,元素之间用逗号隔开。

1
2
3
4
5
6
7
8
9
10
11
12
>>> tup = ('a', 'b', 'c' , 'd')
>>> tup[0]
'a'
>>> tup[1:3]
('b', 'c')
>>> tup[3:]
('d',)
>>> tup[-1]
'd'
>>> tup[-2]
'c'
>>>

元组的操作

1
2
3
>>> dir(tup)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
>>>

可见有countindex

tuple.count

元素元组个数

1
2
3
4
>>> tup = ('a', 'b', 'a')
>>> tup.count('a')
2
>>>

tuple.index

获取元组元素第一个匹配项的索引

1
2
3
4
>>> tup = ('a', 'b', 'a')
>>> tup.index('a')
0
>>>

Set(集合)

集合(set)是一个无序不重复元素的序列。
基本功能是进行成员关系测试和删除重复元素。
可以使用大括号({})或者 set()函数创建集合,但是如果要创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

1
2
3
4
>>> st = set('abcdaaabbbcccddd')
>>> print(st)
{'c', 'd', 'a', 'b'}
>>>

集合运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> st1 = set('abcdefgabc')
>>> st2 = set('abcopqmnaqmn')
>>> st1
{'e', 'b', 'c', 'd', 'a', 'g', 'f'}
>>> st2
{'b', 'o', 'p', 'c', 'm', 'a', 'q', 'n'}
>>> st1 - st2 # 集合st1包含有而集合st2不包含有的
{'d', 'f', 'g', 'e'}
>>> st1 | st2 # 集合st1和st2的合集
{'e', 'b', 'o', 'p', 'c', 'm', 'd', 'a', 'g', 'q', 'f', 'n'}
>>> st1 & st2 # 集合st1和st2的交集
{'a', 'c', 'b'}
>>> st1 ^ st2 # 不同时在st1和st2
{'e', 'f', 'o', 'p', 'm', 'd', 'g', 'q', 'n'}
>>>

set.add

集合添加

1
2
3
4
5
6
7
>>> st = set(('a', 'b', 'a'))
>>> st
{'b', 'a'}
>>> st.add('c')
>>> st
{'c', 'b', 'a'}
>>>

set.update

更新元素

1
2
3
4
5
6
>>> st
{'c', 'b', 'a'}
>>> st.update({1, 2, 3})
>>> st
{1, 2, 3, 'a', 'c', 'b'}
>>>

set.remove

删除元素(如果元素不存在是抛出错误)

1
2
3
4
5
6
7
8
9
10
>>> st
{1, 2, 3, 'a', 'c', 'b'}
>>> st.remove('a')
>>> st
{1, 2, 3, 'c', 'b'}
>>>
>>> st.remove('d')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'd'

set.dicard

删除元素(如果元素不存在不会抛出错误

1
2
3
4
5
6
7
8
9
>>> st
{1, 2, 3, 'c', 'b'}
>>> st.discard(1)
>>> st
{2, 3, 'c', 'b'}
>>> st.discard('d')
>>> st
{2, 3, 'c', 'b'}
>>>

set.clear

清空集合

1
2
3
4
5
6
>>> st
{2, 3, 'c', 'b'}
>>> st.clear()
>>> st
set()
>>>

set.copy

复制集合

1
2
3
4
5
6
7
>>> st = set('abc')
>>> st
{'c', 'b', 'a'}
>>> st1=st.copy()
>>> st1
{'c', 'b', 'a'}
>>>

set.difference

获取两个集合的差集

1
2
3
4
5
6
7
>>> st
>>> st = set('abc')
>>> st2 = set('abd')
>>> st.difference(st2)
{'c'}
>>> st
{'c', 'b', 'a'}

set.difference_update

获取两个集合的差集并更新当前的集合为差集值

1
2
3
4
5
>>> st1 = set('abc')
>>> st2 = set('abd')
>>> st1.difference_update(st2)
>>> st1
{'c'}

set.intersection

返回集合的交集

1
2
3
4
5
6
7
8
9
10
11
12
>>> st = set('abc')
>>> st2 = set('abd')
>>> st.difference(st2)
{'c'}
>>> st
{'c', 'b', 'a'}
>>> st2
{'b', 'd', 'a'}
>>>
>>> st.intersection(st2)
{'b', 'a'}
>>>

set.intersection_update

将集合的交集更新为当前集合的值

1
2
3
4
5
6
7
8
9
10
>>> st
{'c', 'b', 'a'}
>>> st2
{'b', 'd', 'a'}
>>> st.intersection_update(st2)
>>> st
{'b', 'a'}
>>> st2
{'b', 'd', 'a'}
>>>

set.pop

随机删除集合的元素

1
2
3
4
5
6
7
>>> st = set('123456789')
>>> st.pop()
'9'
>>> st
{'3', '7', '1', '5', '2', '6', '8', '4'}
>>> st.pop()
'3'

Dictionary(字典)

字典是一种映射类型,字典用”{ }”标识,它是一个无序的键(key) : 值(value)对集合。

1
2
3
4
5
6
7
8
9
10
11
>>> m = {}
>>> m['name'] = 'farmer'
>>> m['age'] = 20
>>> m['home'] = 'BJ'
>>> print(m)
{'name': 'farmer', 'age': 20, 'home': 'BJ'}
>>> m['age']
20
>>> m['name']
'farmer'
>>>

dict.keys

获取字典的key

1
2
3
4
>>> print(m)
{'name': 'farmer', 'age': 20, 'home': 'BJ'}
>>> m.keys()
dict_keys(['name', 'age', 'home'])

dict.iterms

获取字典的可遍历的元组第一位是key,第二位是value (key, value)

1
2
3
4
>>> print(m)
{'name': 'farmer', 'age': 20, 'home': 'BJ'}
>>> m.items()
dict_items([('name', 'farmer'), ('age', 20), ('home', 'BJ')])

dict.values

返回字典的值

1
2
3
4
>>> print(m)
{'name': 'farmer', 'age': 20, 'home': 'BJ'}
>>> m.values()
dict_values(['farmer', 20, 'BJ'])

修改字典

1
2
3
4
5
6
>>> print(m)
{'name': 'farmer', 'age': 20, 'home': 'BJ'}
>>> m['name'] = 'Farmer'
>>> print(m)
{'name': 'Farmer', 'age': 20, 'home': 'BJ'}
>>>

删除字典元素

1
2
3
4
5
>>> print(m)
{'name': 'Farmer', 'age': 20, 'home': 'BJ'}
>>> del m['name']
>>> print(m)
{'age': 20, 'home': 'BJ'}

python运算符

Python语言支持以下类型的运算符:

  • 算术运算符
  • 比较(关系)运算符
  • 赋值运算符
  • 逻辑运算符
  • 位运算符
  • 成员运算符
  • 身份运算符

算术运算

运算符描述
+加 – 两个对象相加
-减 - 两个对象相减
*乘 - 两个对象相乘
/除 - 两个对象相除
%取余 - 两个对象相除的余数
**幂 - (m**n m的n次方)
//整除 - 商的整数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/local/python3/bin/python3
a = 5
b = 2
c = 2.0

print('a + b的值为: ', a+b)
print('a * b的值为: ', a*b)
print('a / b的值为: ', a/b)
print('a / c的值为: ', a/c)
print('a ** b的值为: ', a**b)
print('a % b的值为: ', a%b)
print('a % c的值为: ', a%c)
print('a // b的值为: ', a/b)
print('a // c的值为: ', a/c)

运行结果

1
2
3
4
5
6
7
8
9
a + b的值为:  7
a * b的值为: 10
a / b的值为: 2.5
a / c的值为: 2.5
a ** b的值为: 25
a % b的值为: 1
a % c的值为: 1.0
a // b的值为: 2.5
a // c的值为: 2.5

Python 比较运算

运算符描述
==等于(比较对象是否相等)
!=不等于(比较两个对象是否不相等)
>大于
<小于
>=大于等于
<=小于等于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/local/python3/bin/python3
a = 3
b = 2
c = 0

if ( a == b ):
print ("1 - a 等于 b")
else:
print ("1 - a 不等于 b")
if ( a != b ):
print ("2 - a 不等于 b")
else:
print ("2 - a 等于 b")
if ( a < b ):
print ("3 - a 小于 b")
else:
print ("3 - a 大于等于 b")
if ( a > b ):
print ("4 - a 大于 b")
else:
print ("4 - a 小于等于 b")


##########################
a = 2;
b = 3;
if ( a <= b ):
print ("5 - a 小于等于 b")
else:
print ("5 - a 大于 b")
if ( b >= a ):
print ("6 - b 大于等于 b")
else:
print ("6 - b 小于 b")

执行结果

1
2
3
4
5
6
1 - a 不等于 b
2 - a 不等于 b
3 - a 大于等于 b
4 - a 大于 b
5 - a 小于等于 b
6 - b 大于等于 b

Python 赋值运算

运算符描述例子
=简单的赋值运算a = b 将b的值赋值为a
+=加法赋值运算符a+=b 等效于 a = a+b
-=减法赋值运算符a-=b 等效于 a = a-b
*=乘法赋值运算符a*=b 等效于 a = a*b
/=除法赋值运算符a/=b 等效于 a = a/b
%=取模赋值运算符a%=b 等效于 a = a%b
**=幂赋值运算符a**=b 等效于 a = a**b
//=取整除赋值运算符a//=b 等效于 a = a//b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/local/python3/bin/python3
a = 5
b = 2
c = 0

c = a + b
print ("1 - c 的值为:", c)
c += a
print ("2 - c 的值为:", c)
c *= a
print ("3 - c 的值为:", c)
c /= a
print ("4 - c 的值为:", c)
c = 2
c %= a
print ("5 - c 的值为:", c)
c **= a
print ("6 - c 的值为:", c)
c //= a
print ("7 - c 的值为:", c)

运行结果

1
2
3
4
5
6
7
1 - c 的值为: 7
2 - c 的值为: 12
3 - c 的值为: 60
4 - c 的值为: 12.0
5 - c 的值为: 2
6 - c 的值为: 32
7 - c 的值为: 6

Python的位运算

运算符描述
&按位与运算符: 参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
竖杠按位或运算符:只要对应的两个二进位有一个为1时,结果为就为1
^按位异或运算符: 当两个对应的二进位相异时,结果为1
~按位取反运算符: 对数据的每个二进制位取反,即把1变为0,把0变为1 (~n = -(n+1))
<<左位移运算符: 运算数的各二进位全部左移若干位,由”<<”右边的数指定移动的位数,高位丢弃,低位补0
>>右移动运算符: 把“>>” 左边的运算数的各二进位全部右移若干位, ”>>” 右边的数指定移动的位数
1
2
3
4
5
6
7
8
9
10
#!/usr/local/python3/bin/python3
a = 30 # 0001 1110
b = 9 # 0000 1001

print('a & b的值为:', a&b)
print('a | b的值为:', a|b)
print('a ^ b的值为:', a^b)
print('a ~ b的值为:', a~b)
print('a << b的值为:', a<<b)
print('a >> b的值为:', a>>b)

运行结果

1
2
3
4
5
6
a & b的值为:  8 # 0000 1000
a | b的值为: 31 # 0001 1111
a ^ b的值为: 23 # 0001 0111
~ a的值为: -31 # -0001 1111
a << b的值为: 15360 # 11 1100 0000 0000
a >> b的值为: 0

Python逻辑运算

运算符表达式描述
andx and y“与”, x y均为True才返回True,否则返回False
orx or y”或“, x y 至少有一个为True返回True,否则返回False
notnot x”非”, x为True 返回False, x为False返回True

Python成员运算

运算符描述
in如果指定的序列中找到值返回True,否则返回False
not in如果指定的序列中没有找到值返回True,否则返回False
1
2
3
4
5
6
7
#!/usr/local/python3/bin/python3


print("F 是否在 Farmer 中: ", 'F' in 'Farmer')
print("f 是否在 Farmer 中: ", 'f' in 'Farmer')
print("a 是否不在 Farmer 中: ", 'a' not in 'Farmer')
print("b 是否不在 Farmer 中: ", 'b' not in 'Farmer')

运行结果

1
2
3
4
F 是否在 Farmer 中:  True
f 是否在 Farmer 中: False
a 是否不在 Farmer 中: False
b 是否不在 Farmer 中: True

Python身份运算

运算符描述
isis是判断两个标识符是不是引用同一个对象
is notis not 是判断两个标识符是不是引用不同的对象
1
2
3
4
5
6
7
8
9
10
#!/usr/local/python3/bin/python3
a = 10
b = 10
print("a 和 b 是否引用同一个对象: ", a is b)
print("a 和 b 是否没有引用同一个对象: ", a is not b)

b = 8

print("a 和 b 是否引用同一个对象: ", a is b)
print("a 和 b 是否没有引用同一个对象: ", a is not b)

执行结果

1
2
3
4
ab 是否引用同一个对象:  True
ab 是否没有引用同一个对象: False
ab 是否引用同一个对象: False
ab 是否没有引用同一个对象: True

Python if判断

Python if判断基本形式如下

1
2
3
4
if 判断条件:
执行语句……
else
执行语句……

或者

1
2
3
4
5
6
if 判断条件:
执行语句……
elif 判断条件:
执行语句……
else
执行语句……

if … else

1
2
3
4
5
6
7
8
9
# -*- coding:utf-8 -*-

a = 3
b = 2

if a > b:
print("a 大于 b")
else:
print("a 不大于 b")

执行结果

1
a 大于 b

if… elif .. else

1
2
3
4
5
6
7
8
9
10
11
# -*- coding:utf-8 -*-

a = 3
b = 2

if a < b:
print("a 小于 b")
elif a == 3:
print("a 等于 3")
else:
print("a 不小于于 b")

执行结果

1
a 等于 3

if … and … else …

1
2
3
4
5
6
7
8
9
# -*- coding:utf-8 -*-

a = 3
b = 2

if a+b > 4 and a+b < 6:
print("a+b 大于 4 小于 6")
else:
print("a+b<=4 或者 a+b>=6")

执行结果

1
a+b 大于 4 小于 6

if … or … else …

1
2
3
4
5
6
7
8
9
# -*- coding:utf-8 -*-

a = 3
b = 2

if a > 5 or b < 6:
print(True)
else:
print(False)

执行结果

1
True

Python 循环

循环类型描述
while在给定的判断条件为 true 时执行循环体,否则退出循环体。
for 循环重复执行语句
嵌套循环你可以在while循环体中嵌套for循环

循环控制

控制语句描述
break在语句块执行过程中终止循环,并且跳出整个循环
continue在语句块执行过程中终止当前循环,跳出该次循环,执行下一次循环。
passpass是空语句,是为了保持程序结构的完整性。

While循环

1
2
3
4
5
6
7
8
9
# -*- coding:utf-8 -*-

num = 100
start = 0
sum = 0
while start <= num:
sum += start
start += 1
print("1+2+3+...+100的和为: ", sum)

执行结果

1
1+2+3+...+100的和为:  5050

while 无限循环

1
2
3
4
5
6
7
8
9
#!/usr/local/python3/bin/python3
# -*- coding:utf-8 -*-

while True:
num = (input("请输入一个数字: "))
if num.isdigit():
print("您输入的数字是: ", num)
else:
print("您输入的不是数字,请重新输入")

执行结果

1
2
3
4
5
6
7
8
请输入一个数字: 1
您输入的数字是: 1
请输入一个数字: 2
您输入的数字是: 2
请输入一个数字: a
您输入的不是数字,请重新输入
请输入一个数字: 1
您输入的数字是: 1

While 循环增加break

1
2
3
4
5
6
7
8
9
#!/usr/local/python3/bin/python3
# -*- coding:utf-8 -*-

while True:
num = (input("请输入一个数字: "))
if num.isdigit():
print("您输入的数字是: ", num)
else:
break

执行结果

1
2
3
4
5
请输入一个数字: 1
您输入的数字是: 1
请输入一个数字: 3
您输入的数字是: 3
请输入一个数字: a # 退出循环

数字循环的脚本如下

1
2
3
4
5
6
7
8
# -*- coding:utf-8 -*-

a = 0
while a <= 100:
a += 1
print("a的值为:", a)
if a == 10:
break

执行结果

1
2
3
4
5
6
7
8
9
10
11
a的值为: 1
a的值为: 2
a的值为: 3
a的值为: 4
a的值为: 5
a的值为: 6
a的值为: 7
a的值为: 8
a的值为: 9
a的值为: 10
[Finished in 0.3s]

While 循环continue

1
2
3
4
5
6
7
8
# -*- coding:utf-8 -*-

a = 0
while a <= 10:
a += 1
if a == 5:
continue
print("当前a的值为:", a)

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
当前a的值为: 1
当前a的值为: 2
当前a的值为: 3
当前a的值为: 4
## 少一个数字5
当前a的值为: 6
当前a的值为: 7
当前a的值为: 8
当前a的值为: 9
当前a的值为: 10
当前a的值为: 11
[Finished in 0.4s]

While 循环pass

1
2
3
4
5
6
7
8
# -*- coding:utf-8 -*-

a = 0
while a <= 10:
a += 1
if a == 5:
pass
print("当前a的值为:", a)

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
当前a的值为: 1
当前a的值为: 2
当前a的值为: 3
当前a的值为: 4
当前a的值为: 5
当前a的值为: 6
当前a的值为: 7
当前a的值为: 8
当前a的值为: 9
当前a的值为: 10
当前a的值为: 11
[Finished in 0.5s]

for 循环

任何有序列的元素都可以用for循环

1
2
3
4
5
# -*- coding:utf-8 -*-

a = [1, 2, 3, 4, 5, 6]
for i in a:
print(i)

执行结果

1
2
3
4
5
6
1
2
3
4
5
6

for 循环 嵌套 if判断

1
2
3
4
5
6
7
8
# -*- coding:utf-8 -*-

a = [1, 2, 3, 4, 5, 6]
for i in a:
if i == 5:
print("I Got Number 5")
else:
print("This Number is Not 5, It is ", i)

执行结果

1
2
3
4
5
6
This Number is Not 5, It is  1
This Number is Not 5, It is 2
This Number is Not 5, It is 3
This Number is Not 5, It is 4
I Got Number 5
This Number is Not 5, It is 6

for 循环break

1
2
3
4
5
6
7
8
# -*- coding:utf-8 -*-

for num in range(1,10):
if num%2 == 0:
print(num, " 是偶数")
break
else:
print(num, " 是质数")

执行结果

1
2
1  是质数
2 是偶数

如果不加break

1
2
3
4
5
6
7
# -*- coding:utf-8 -*-

for num in range(1,10):
if num%2 == 0:
print(num, " 是偶数")
else:
print(num, " 是质数")

执行结果

1
2
3
4
5
6
7
8
9
1  是质数
2 是偶数
3 是质数
4 是偶数
5 是质数
6 是偶数
7 是质数
8 是偶数
9 是质数

Python日期

time模块

当前时间(浮点型格式)

1
2
3
4
# -*- coding:utf-8 -*-
import time

print(time.time())

执行结果

1
1551428176.7746024

获取直观的时间格式

1
2
3
4
# -*- coding:utf-8 -*-
import time

print(time.ctime())

执行结果

1
Fri Mar  1 16:19:05 2019

浮点型时间转换成直观的时间格式

1
2
3
4
# -*- coding:utf-8 -*-
import time
cur = time.time()
print(time.ctime(cur))

执行结果
Fri Mar 1 17:08:21 2019

localtime()

获取当前时间

1
2
3
# -*- coding:utf-8 -*-
import time
print(time.localtime())

执行结果
time.struct_time(tm_year=2019, tm_mon=3, tm_mday=1, tm_hour=17, tm_min=9, tm_sec=24, tm_wday=4, tm_yday=60, tm_isdst=0)

gmtime()

获取UTC时间

1
2
3
# -*- coding:utf-8 -*-
import time
print(time.gmtime())

执行结果
time.struct_time(tm_year=2019, tm_mon=3, tm_mday=1, tm_hour=9, tm_min=13, tm_sec=51, tm_wday=4, tm_yday=60, tm_isdst=0)

解析UTC格式时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding:utf-8 -*-
import time

t = time.gmtime()

print('当前年份:',t.tm_year)
print('当前月份:',t.tm_mon)
print('今天的日期:',t.tm_mday)
print('当前的小时:',t.tm_hour)
print('当前的分钟',t.tm_min)
print('当前的秒',t.tm_sec)
print('当前的星期:',t.tm_wday)
print('一年的第 %s 天' % t.tm_yday)
print('是否夏令时:',t.tm_isdst)

执行结果

1
2
3
4
5
6
7
8
9
当前年份: 2019
当前月份: 3
今天的日期: 1
当前的小时: 9
当前的分钟 17
当前的秒 29
当前的星期: 4
一年的第 60
是否夏令时: 0

日期格式化

1
2
3
4
5
6
# -*- coding:utf-8 -*-
import time

t = time.localtime()
ft = time.strftime("%Y-%m-%d %H:%M:%S", t)
print(ft)

执行结果
2019-03-01 17:24:25

日期格式化参数表

标识含义举例
%a星期简写Mon
%A星期全称Monday
%b月份简写Mar
%B月份全称March
%c适合语言下的时间表示May Mon May 20 16:00:02 2013
%d一个月的第一天,取值范围: [01,31].20
%H24小时制的小时,取值范围[00,23].17
%I12小时制的小时,取值范围 [01,12].10
%j一年中的第几天,取值范围 [001,366].120
%m十进制月份,取值范围[01,12].50
%M分钟,取值范围 [00,59].50
%p上、下午,AM 或 PM.PM
%S秒,取值范围 [00,61].30
%U这一年的星期数(星期天为一个星期的第一天,开年的第一个星期天之前的天记到第0个星期)趋势范围[00,53].20
%w星期的十进制表示,取值范围 [0(星期天),6].1
%W这一年的星期数(星一为一个星期的第一天,开年的第一个星期一之前的天记到第0个星期)趋势范围[00,53].20
%x特定自然语言下的日期表示05/20/13
%X特定自然语言下的时间表示16:00:02
%y年的后两位数,取值范围[00,99].13
%Y完整的年2013
%Z时区名CST(China Standard Time)
%%%字符%

时间、日期、时间戳的转换

时间字符串转换时间组和时间戳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding:utf-8 -*-
import time

# 字符类型的时间
t_str = '2019-03-01 17:30:00'

# 转为时间数组
tArray = time.strptime(t_str, "%Y-%m-%d %H:%M:%S")
print("时间数组为: ", tArray)

# timeArray可以调用tm_year等
print("时间数组的年份:", tArray.tm_year)

# 转为时间戳
timeStamp = int(time.mktime(tArray))
print("转换为时间戳为: ", timeStamp)

执行结果为

1
2
3
时间数组为:  time.struct_time(tm_year=2019, tm_mon=3, tm_mday=1, tm_hour=17, tm_min=30, tm_sec=0, tm_wday=4, tm_yday=60, tm_isdst=-1)
时间数组的年份: 2019
转换为时间戳为: 1551432600

时间戳转换为指定的格式日期

1
2
3
4
5
6
7
# -*- coding:utf-8 -*-
import time

timeStamp = 1551432600
timeArray = time.localtime(timeStamp)
ft = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
print("时间戳 1551432600 格式化后的日期为:", ft)

执行结果

1
时间戳 1551432600 格式化后的日期为: 2019-03-01 17:30:00

datime 模块

datetime 模块为日期和时间处理提供了多种方法。支持方法的同时,还可格式化输出。此外,该模块还支持时区的处理。

datetime.date类

自定义时间

1
2
3
4
5
# -*- coding:utf-8 -*-
import datetime

d = datetime.date(2019, 3, 1)
print(d)

执行结果

1
2019-03-01

获取当前日期

1
2
3
4
5
# -*- coding:utf-8 -*-
import datetime

d = datetime.date.today()
print(d)

执行结果

1
2019-03-01

获取星期几

1
2
3
4
5
# -*- coding:utf-8 -*-
import datetime
t = datetime.date.today() # '2019-03-01'
d = datetime.date.weekday(t)
print(d)

执行结果
4

解析年 月 日

1
2
3
4
5
6
# -*- coding:utf-8 -*-
import datetime
t = datetime.date.today() # 2019-03-01
print(t.year)
print(t.month)
print(t.day)

执行结果

1
2
3
2019
3
1

返回直观的当前日期

1
2
3
4
# -*- coding:utf-8 -*-
import datetime

print(datetime.date.ctime(datetime.date.today()))

执行结果

1
Fri Mar  1 00:00:00 2019

根据时间戳返回date对象

1
2
3
4
# -*- coding:utf-8 -*-
import datetime

print(datetime.date.fromtimestamp(1551432600))

执行结果

1
2019-03-01

格式化日期

1
2
import datetime
print(datetime.date.strftime(datetime.date.today(), '%Y/%m/%d %H:%M:%S'))

执行结果

1
2019/03/01 00:00:00

日期time.struct_time对象

1
2
import datetime
print(datetime.date.timetuple(datetime.date.today()))

执行结果

1
time.struct_time(tm_year=2019, tm_mon=3, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=60, tm_isdst=-1)

datetime.time类

datetime.time(hour, minute, second, microsecond, tzoninfo)格式

1
2
3
import datetime

print(datetime.time(19, 50, 10))

执行结果

1
19:50:10

datetime.time.max

时、分、秒、微妙的最大取值

1
2
3
import datetime

print(datetime.time.max)

执行结果

1
2
3

#### datetime.time.min
>时、分、秒、微妙的最小取值

import datetime

print(datetime.time.min)

1
2
3

**执行结果**
```00:00:00

datetime.time.replace

用给定的参数时、分、秒、微秒代替原有对象中的属性,创建一个新的时间对象,但原有对象仍保持不变

1
2
3
4
5
6
7
8
9
import datetime

tm = datetime.time(9, 5, 30, 6666)
print("Replace之前的时间值: ", tm)
tmr = tm.replace(hour=10, minute=50, second=45, microsecond=8888)

print("Replace之后的时间值: ", tmr)

print("Replace之前的时间值: ", tm)

执行结果

1
2
3
Replace之前的时间值:  09:05:30.006666
Replace之后的时间值: 10:50:45.008888
Replace之前的时间值: 09:05:30.006666

time.isoformat

返回时间格式如:HH:MM:SS的时间字符串表示

1
2
3
4
import datetime

tm = datetime.time(9, 5, 30, 6666)
print(tm.isoformat())

time.strftime

返回自定义格式时间字符串

1
2
3
4
import datetime

tm = datetime.time(9, 5, 30, 6666)
print(tm.strftime("%H--%M--%S"))

timedelta模块

timedelta.max正值的最大

1
2
3
import datetime

print(datetime.timedelta.max)

执行结果

1
999999999 days, 23:59:59.999999

timedelta.max.days

天数的最大值

1
2
3
import datetime

print(datetime.timedelta.max.days)

执行结果

1
999999999

timedelta.max.seconds

秒的最大

1
2
3
import datetime

print(datetime.timedelta.max.seconds)

执行结果

1
86399

timedelta.max.microseconds

微秒最大

1
2
3
import datetime

print(datetime.timedelta.max.microseconds)

执行结果

1
999999

timedelta.min最小

1
2
3
import datetime

print(datetime.timedelta.min)

执行结果

1
-999999999 days, 0:00:00

timedelta.min.days

天数的最小值

1
2
3
import datetime

print(datetime.timedelta.min.days)

执行结果

1
-999999999

timedelta.min.seconds

秒的最小值

1
2
3
import datetime

print(datetime.timedelta.min.seconds)

执行结果

1
2
3

#### timedelta.min.microseconds
>微秒的最小值

import datetime

print(datetime.timedelta.min.microseconds)

1
2
3

**执行结果**
```0

timedelta计算一天前的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_day_before = now - datetime.timedelta(days=1)
print("当前时间为: ", now)
print("一天前时间:", one_day_before)

执行结果

1
2
当前时间为:  2019-03-04 10:03:17.716686
一天前时间: 2019-03-03 10:03:17.716686

timedelta计算一天后的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_day_later = now + datetime.timedelta(days=1)
print("当前时间为: ", now)
print("一天后时间:", one_day_later)

执行结果

1
2
当前时间为:  2019-03-04 10:04:35.424897
一天后时间: 2019-03-05 10:04:35.424897

timedelta计算1小时前的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_hour_before = now - datetime.timedelta(hours=1)
print("当前的时间为: ", now)
print("一小时前时间:", one_hour_before)

执行结果

1
2
当前的时间为:  2019-03-04 10:05:53.226939
一小时前时间: 2019-03-04 09:05:53.226939

timedelta计算1小时后的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_hour_later = now + datetime.timedelta(hours=1)
print("当前的时间为: ", now)
print("一小时后时间:", one_hour_later)

执行结果

1
2
当前的时间为:  2019-03-04 10:06:39.583865
一小时后时间: 2019-03-04 11:06:39.583865

timedelta计算1小时30分钟前的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_hour_before = now - datetime.timedelta(hours=1, minutes=30)
print("当前的时间为: ", now)
print("一小时30分钟1前的时间:", one_hour_before)

执行结果

1
2
当前的时间为:  2019-03-04 10:10:59.098280
一小时30分钟前的时间: 2019-03-04 08:40:59.098280

timedelta计算1小时30分钟后的时间

1
2
3
4
5
6
7
import datetime

now = datetime.datetime.now()

one_hour_later = now + datetime.timedelta(hours=1, minutes=30)
print("当前的时间为: ", now)
print("一小时30分钟后的时间:", one_hour_later)

执行结果

1
2
当前的时间为:  2019-03-04 10:11:59.832085
一小时30分钟后的时间: 2019-03-04 11:41:59.832085

timedelta计算总的秒数

1
2
3
4
import datetime

total = datetime.timedelta(hours=1, minutes=30).total_seconds()
print("1小时30分钟总的秒数为: ", total, "类型为: ", type(total))

执行结果

1
1小时30分钟总的秒数为:  5400.0 类型为:  <class 'float'>

datetime模块

datetime.today 当前本地时间

1
2
3
import datetime

print(datetime.datetime.today())

执行结果

1
2019-03-04 11:01:28.771064

datetime.now 当前本地时间,可以设置参数时区(tz)

1
2
3
import datetime

print(datetime.datetime.now())

执行结果

1
2019-03-04 10:58:04.656114

设置时区

1
2
3
4
import datetime
import pytz

print(datetime.datetime.now(tz=pytz.timezone('US/Hawaii')))

执行结果

1
2019-03-03 17:26:44.604929-10:00

时区的转换

1
2
3
4
5
6
7
8
import datetime
import pytz

tz = pytz.timezone('Asia/Shanghai')
t = datetime.datetime(2019, 3, 4, 11, 38, 50, tzinfo=tz)
x = t.astimezone(pytz.utc)
print(x)
print(x.astimezone())

执行结果

1
2
2019-03-04 03:32:50+00:00
2019-03-04 11:32:50+08:00

Python function函数

函数是 Python 程序的重要组成单位,一个 Python 程序可以由很多个函数组成,函数可以接收零个或者多个参数,可以返回零个或者多个值。

函数的定义

定义函数的基本语法如下

1
2
3
def 函数名(形参列表):
//由零条到多条可执行语句组成的函数
[return [返回值]]
  • Python 声明函数必须使用def关键字。
  • 函数名只要是一个合法的标识符即可;从程序的可读性角度来看,函数名应该由一个或多个有意义的单词连缀而成,每个单词的字母全部小写,单词与单词之间使用下画线分隔。
  • 参数由多个形参名组成,多个形参名之间以英文逗号(,)隔开。

如下简单的定义一个加法的函数

1
2
3
4
def add(x, y):
print("x的值为: ", x)
print("y的值为: ", y)
return x + y

函数的调用

函数的调用就是直接使用函数的名字加上(),如果有参数,将参数放到小括号内,使用参数注意顺序,如果没有顺序的话,要添加变量名和值。

按照关键字传参数

1
2
3
4
5
6
7
8
def add(x, y):
print("x的值为: ", x)
print("y的值为: ", y)
return x + y


print(add(2, 3))
print(add(y=5, x=8))

执行结果

1
2
3
4
5
6
x的值为:  2
y的值为: 3
5
x的值为: 8
y的值为: 5
13

按照默认值传参数

1
2
3
4
5
6
7
8
9
10
def add(x, y=8):
print("x的值为: ", x)
print("y的值为: ", y)
return x + y


print(add(2, 3))
print(add(2))
print(add(y=5, x=8))
print(add(x=8))

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
x的值为:  2
y的值为: 3
5
x的值为: 2
y的值为: 8
10
x的值为: 8
y的值为: 5
13
x的值为: 8
y的值为: 8
16

不定长的参数

不定长参数在参变量前加一个 *

1
2
3
4
5
6
7
def add(x, *y):
print("x的值为: ", x)
for i in y:
print("不定长参数的参数值为:", i)


add(8, 9, 10, 11)

执行结果

1
2
3
4
x的值为:  8
不定长参数的参数值为: 9
不定长参数的参数值为: 10
不定长参数的参数值为: 11

如果将不定长参数放在前面的调用

需要指定非不定长参变量的关键值

1
2
3
4
5
6
7
def add(*y, x):
print("x的值为: ", x)
for i in y:
print("不定长参数的参数值为:", i)


add(8, 9, 10, x=11)

执行结果

1
2
3
4
x的值为:  11
不定长参数的参数值为: 8
不定长参数的参数值为: 9
不定长参数的参数值为: 10

逆向的参数

在程序己有列表、元组、字典等对象的前提下,把它们的元素“拆开”后传给函数的参数
如果是字典,需要在字典前写两个 *

传值list列表

1
2
3
4
5
6
7
8
def add(x, y):
print("x的值为: ", x)
print("y的值为: ", y)


num = [8, 9]

add(*num)

执行结果

1
2
x的值为:  8
y的值为: 9

给不定长参变量传值 list

1
2
3
4
5
6
7
8
9
def add(x, *y):
print("x的值为: ", x)
for i in y:
print("不定长变量: ", i)


num = [8, 9]

add(3, *num)

执行结果

1
2
3
x的值为:  3
不定长变量: 8
不定长变量: 9

也可以如下

1
2
3
4
5
6
7
8
9
def add(x, *y):
print("x的值为: ", x)
for i in y:
print("不定长变量: ", i)


num = [8, 9, 10]

add(*num)

执行结果

1
2
3
x的值为:  8
不定长变量: 9
不定长变量: 10

同样,如果不定长变量在前面

1
2
3
4
5
6
7
8
9
def add(*y, x):
print("x的值为: ", x)
for i in y:
print("不定长变量: ", i)


num = [8, 9]

add(x=3, *num)

执行结果

1
2
3
x的值为:  3
不定长变量: 8
不定长变量: 9

逆向传入字典

1
2
3
4
5
6
7
def add(name, age, home):
print("我的名字:", name, ", 我今年:", age, " 岁, ", "我的家在: ", home)


di = {"name": "farmer", "age": 20, "home": "BJ"}

add(**di)

执行结果

1
我的名字: farmer , 我今年: 20  岁,  我的家在:  BJ

lambda 匿名函数

lambda只是一个表达式,函数体比def简单很多,很多时候定义def,然后写一个函数太麻烦,这时候就可以用lambda定义一个匿名函数

1
2
d = list(filter(lambda x: True if x % 3 == 0 else False, range(100)))
print(d)

filter(函数,序列)函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。

执行结果

1
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]

函数

1
2
a = lambda x: x+2
print(a(2))

等同于

1
2
def a(x):
return(x+2)

简化函数

1
2
3
4
5
def a(x, y):
return lambda: x + y


print(a(2, 3)())

注意执行的时候最后还有一个小括号(),因为lambda是一个匿名函数,函数调用要加这个

Python 类和对象

在设计之初,Python 就被设计成支持面向对象的编程语言,因此 Python 完全能以面向对象的方式编程。而且 Python 的面向对象比较简单,它不像其他面向对象语言提供了大量繁杂的面向对象特征,它致力于提供简单、够用的语法功能。

正因为如此,在 Python 中创建一个类和对象都很容易。Python 支持面向对象的三大特征:封装、继承和多态,子类继承父类同样可以继承到父类的变量和方法。

class 类

类的定义语法如下

1
2
3
4
class 类名:
执行语句...
零个到多个类变量...
零个到多个方法...

类名只要是一个合法的标识符即可,但这仅仅满足的是 Python 的语法要求:如果从程序的可读性方面来看,Python 的类名必须是由一个或多个有意义的单词连缀而成的,每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。

例如定义一个空类

1
2
class Test:
pass

这就是一个空类,但是空类没有意义

在类中定义的方法默认是实例方法,定义实例方法的方法与定义函数的方法基本相同,只是实例方法的第一个参数会被绑定到方法的调用者(该类的实例),因此实例方法至少应该定义一个参数,该参数通常会被命名为 self。

在实例方法中有一个特别的方法:init,这个方法被称为构造方法。init 在类中的执行顺序优先于其他,也就是说,在调用类的时候,先执行的是init,如果没有init则执行其他

如下定义个类,参数由类传值

1
2
3
4
5
6
7
8
9
10
11
12
13
class My:
def __init__(self, name, age, home):
self.name = name
self.age = age
self.home = home

def hello(self):
print("My name is: %s, My age is %d, My home is %s" % (self.name, self.age, self.home))

# 将my对象赋值给 m
m = My("farmer", 20, "BJ")

m.hello()

执行结果

1
My name is: farmer, My age is 20, My home is BJ

如下定义一个类,参数可变,不输入参数有默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class My:
def __init__(self, name="farmer", age=20, home="BJ"):
self.name = name
self.age = age
self.home = home

def hello(self):
print("My name is: %s, My age is %d, My home is %s" % (self.name, self.age, self.home))


m = My()
# 使用默认值
m.hello()
# 使用自己定义的值
m.name = "Bill"
m.age = 18
m.home = "SH"

m.hello()

执行结果

1
2
My name is: farmer, My age is 20, My home is BJ
My name is: Bill, My age is 18, My home is SH

类定义中的self

self的变量可以在其他的方法中使用

如下

1
2
3
4
5
6
7
calss AA:
def B(self):
print("I am B")

def C(self):
self.B()
print("I am C, running B")

静态类和类方法

使用 @classmethod 修饰的方法就是类方法;使用 @staticmethod 修饰的方法就是静态方法。调用类方法,会自动绑定第一个参数,调用静态类,则需要手动绑定参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class My:
def __init__(self, name="farmer"):
self.name = name

@classmethod
def hello(cls):
print("类方法 hello: ", cls)

@staticmethod
def st(static):
print("静态方法st: ", static)


My.hello()
My.st("---static----")

m = My()
m.hello()
m.st("---I am Static---")

执行结果

1
2
3
4
类方法 hello:  <class '__main__.My'>
静态方法st: ---static----
类方法 hello: <class '__main__.My'>
静态方法st: ---I am Static---

Python 装饰器

装饰器是一个函数,他的参数也是一个函数,返回是一个函数,他用来修饰其他函数,修饰被修饰的函数。

普通的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def a(fn):
print("I am A")
# 执行fn函数
fn()
# 最后返回
return "A is End"


# 执行a函数,参数fn是b函数
@a
def b():
print("I am B")


print(b)

执行结果

1
2
3
I am A
I am B
A is End
  • 上述程序使用 a修饰b, 将b作为a的参数,@a相当于a(b), 执行完毕后,b不再是函数,而是被替换成一个字符串(return的结果)

返回函数的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def a(fn):
# 定义嵌套函数
def c(*args):
print("-- Get args -- ", args)
num = args[0]
print("-- num -- ", num)
print("function name ", fn.__name__)
fn(num + 2)
return fn(num + 3)
print("I am Here!")
return c


@a
def b(num):
print("-- function B num value --", num)

# 执行
print(b)
b(2)

执行结果

1
2
3
4
5
6
7
I am Here!
<function a.<locals>.c at x000002009A73B510>
-- Get args -- (2,)
-- num -- 2
function name b
-- function B num value -- 4
-- function B num value -- 5

  • 函数a装饰函数b,同样@a执行的是 a(b), 而b在执行后变成a()函数的返回值 c函数, 其实执行b(2), 就相当于执行 c(2)函数

如果我执行print(b(2))会是什么样的结果?, 这个结果应该是None, 因为 b函数没有return值。所以如果不想执行print(b(2))不是None,可以做如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def a(fn):
# 定义嵌套函数
def c(*args):
print("-- Get args -- ", args)
num = args[0]
print("-- num -- ", num)
print("function name ", fn.__name__)
fn(num + 2)
return fn(num + 3)
print("I am Here!")
return c


@a
def b(num):
print("-- function B num value --", num)
return "Farmer"


print(b)
print(b(2))

执行结果

1
2
3
4
5
6
7
8
I am Here!
<function a.<locals>.c at x00000211767AB510>
-- Get args -- (2,)
-- num -- 2
function name b
-- function B num value -- 4
-- function B num value -- 5
Farmer

只增加被修饰函数的功能

有时间会有这样的问题,例如我们写好一个程序,需要给程序增加一个日志,总不能每个程序都加一遍吧,可以做如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def a(fn):
# 定义嵌套函数
def c():
print("-- 准备运行程序 -- ", fn.__name__)
return fn()
return c


@a
def b():
print("-- function B --")
return "Farmer"


b()

执行结果

1
2
-- 准备运行程序 --  b
-- function B --

类变量

在类中定义的变量,该变量属于类本身。

使用类访问类变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class My:
name = "farmer"
home = "BeiJing"

def person(self):
# 通过类访问变量,对于类变量而言,它们就是属于在类命名空间内定义的变量,
# 因此程序不能直接访问这些变量,必须使用类名来调用类变量
print("My Name is: ", My.name)
print("My Home is: ", My.home)


print(My.name)
m = My()
m.person()

m.name = "FAR"
m.home = "ShangHai"
m.person()

执行结果

1
2
3
4
5
farmer
My Name is: farmer
My Home is: BeiJing
My Name is: farmer
My Home is: BeiJing

使用对象来访问该对象所属类的类变量(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class My:
name = "farmer"
home = "BeiJing"

def person(self):
# 使用对象来访问该对象所属类的类变量
print("My Name is: ", self.name)
print("My Home is: ", self.home)


print(My.name)
m = My()
m.person()

m.name = "FAR"
m.home = "ShangHai"
m.person()

执行结果

1
2
3
4
5
farmer
My Name is: farmer
My Home is: BeiJing
My Name is: FAR
My Home is: ShangHai

通过对象对类变量赋值,其实不是对“类变量赋值”,而是定义新的实例变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class My:
name = "farmer"
home = "BeiJing"

def person(self, name, home):
self.name = name
self.home = home


m = My()
m.person("Bill", "US")
print(m.name)
print(m.home)
print("========")
print(My.name)
print(My.home)

执行结果

1
2
3
4
5
Bill
US
========
farmer
BeiJing

property函数

property() 函数的语法格式如下

1
property(fget=None, fset=None, fdel=None, doc=None)

在使用 property() 函数时,可传入 4 个参数,分别代表 getter 方法、setter 方法、del 方法和 doc,其中 doc 是一个文档字符串,用于说明该属性。
当然,开发者调用 property 也可传入 0 个(既不能读,也不能写的属性)、1 个(只读属性)、2 个(读写属性)、3 个(读写属性,也可删除)和 4 个(读写属性,也可删除,包含文档说明)参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class My:
def __init__(self, name, age):
self.name = name
self.age = age

def set_info(self, info):
self.name, self.age = info

def get_info(self):
return self.name, self.age

def del_info(self):
self.name, self.age = "", 0

info = property(get_info, set_info, del_info, "存放个人信息")


print("================doc============")
print(My.info.__doc__)
print("================help============")
help(My.info)

m = My("farmer", 20)
print("================info============")
print("Info信息: ", m.info)
print("================更新 info============")
m.info = "FAR", 19
print("================info 新的信息============")
print("更新后info的姓名: ", m.name)
print("更新后info的年龄: ", m.age)
print("================删除info============")
del m.info
print("================删除后info信息============")
print("删除后info的姓名: ", m.name)
print("删除后info的年龄: ", m.age)

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
================doc============
存放个人信息
================help============
Help on property:

存放个人信息

================info============
Info信息: ('farmer', 20)
================更新 info============
================info 新的信息============
更新后info的姓名: FAR
更新后info的年龄: 19
================删除info============
================删除后info信息============
删除后info的姓名:
删除后info的年龄: 0

property 作为装饰器

可使用 @property 装饰器来修饰方法,使之成为属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class My:

# 使用@property修饰方法,相当于为该属性设置getter方法
@property
def status(self):
return self._status

# 相当于为该属性设置setter方法
@status.setter
def status(self, value):
if 'running' in value.lower():
self._status = "running"
else:
self._status = "stopped"

@property
def is_running(self):
return self._status.lower() == 'running'


m = My()
m.status = "running"
print(m.status)
print(m.is_running)

执行结果

1
2
running
True

封装

Python没有private 等修饰符,只要将类成员命名以双下划线(__) 开头, 就可以实现隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class My:

def __hidden(self):
print("这是隐藏的,不允许外部访问")

def get_name(self):
return self._name

def set_name(self, name):
self._name = name

name = property(get_name, set_name)

def get_age(self):
return self._age

def set_age(self, age):
self._age = age

age = property(get_age, set_age)


m = My()
m.name = "farmer"
m.age = 20

print("age is: ", m.age)
print("name is: ", m.name)
# 执行到下面 会报错
m.__hidden()

执行结果

1
2
3
4
5
6
age is:  20
name is: farmer
Traceback (most recent call last):
File "D:/python工程/js-j.py", line 35, in <module>
m.__hidden()
AttributeError: 'My' object has no attribute '__hidden'

类继承

类继承语法如下

1
2
class Subclass (SuperClass1, SuperClass2, ...)
#类定义部分

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Name:
def detail(self):
print("姓名是: %s" % self.name)


class Age:
def info(self):
print("年龄是: %s" % self.age)


class User(Name, Age):
pass


m = User()
m.name = "farmer"
m.age = 20

m.detail()
m.info()

执行结果

1
2
姓名是: farmer
年龄是: 20

父类重写

子类扩展父类,以父类为基础,可以额外增加,如果有相同的则会把父类重新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A:
def info(self):
print("My Name is A")


class B(A):
def info(self):
print("My Name is B")

def test(self):
print("This is Add")


m = B()
m.info()
m.test()

执行结果

1
2
My Name is B
This is Add

类的super

子类继承多个父类,那么排在前面的将会被优先使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A:
def __init__(self, name):
self.name = name

def info(self):
print("My Name is ", self.name)


class B:
def __init__(self, age, home):
self.age = age
self.home = home

def address(self):
print("My Age is ", self.age)
print("My Home is ", self.home)


class C(A, B):
pass


m = C("FARMER")
m.info()

执行结果

1
My Name is  FARMER

使用super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A:
def __init__(self, name):
self.name = name

def info(self):
print("My Name is ", self.name)


class B:
def __init__(self, age, home):
self.age = age
self.home = home

def address(self):
print("My Age is ", self.age)
print("My Home is ", self.home)


class C(A, B):
def __init__(self, name, age, home):
super().__init__(name)
B.__init__(self, age, home)


m = C("FARMER", 20, "BeiJing")
m.info()
m.address()

执行结果

1
2
3
My Name is  FARMER
My Age is 20
My Home is BeiJing

类动态添加属性和方法(slots 用来限制)

动态添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Mail:

def __init__(self, name):
self.name = name


def send(self):
print("I send a Email to: ", self.name)


m1 = Mail("Farmer")
m2 = Mail("Bill")

Mail.sd = send
m1.sd()
m2.sd()

执行结果

1
2
I send a Email to:  Farmer
I send a Email to: Bill

slots限制添加

上面的动态添加虽然有很多优势,但也有很多安全隐患,使用slots则可以限制添加,只对当前类起作用,子类不受作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Mail:
__slots__ = ('sd', 'age', 'name')

def __init__(self, name):
self.name = name

def test(self):
print("I send a Email to: ", self.name)


def send(self):
print("我发了一封邮件给: ", self.name)


m = Mail("Lucy")
# 只允许添加3个动态属性或者方法
Mail.sd = send
m.sd()
m.age = 20
m.name = "Farmer"
print(m.age)
print(m.name)
m.make = "Make" # 会报错

执行结果

1
2
3
4
5
我发了一封邮件给:  Lucy
20
Farmer

AttributeError: 'Mail' object has no attribute 'make'

type函数查看类型和创建类

查看类型

1
2
3
4
5
6
7
class Mail:

def __init__(self):
self.name = 1


print(type(Mail()))

执行结果

1
<class '__main__.Mail'>

创建类

1
2
3
4
5
6
7
8
9
10
11
12
def fw(self):
print("我是FW函数")


M = type('M', (object,), dict(hello=fw, name="FARMER"))

m = M()
print(type(m))
print(type(M))

m.hello()
print("Name is: ", m.name)

执行结果

1
2
3
4
<class '__main__.M'>
<class 'type'>
我是FW函数
Name is: FARMER

metaclass

创建某一批类有同一特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyMetaClass(type):
# class_name 类名称
# class_parent 修改类的父类
# 类的属性,方法等字典信息
def __new__(cls, class_name, class_parent, class_attr):
class_attr['money'] = lambda self: self.salary * self.months
return type.__new__(cls, class_name, class_parent, class_attr)


class Farmer(metaclass=MyMetaClass):
__slots__ = ('name', 'salary', '_months')

def __init__(self, name, salary):
self.name = name
self.salary = salary

@property
def months(self):
return self._months

@months.setter
def months(self, months):
self._months = months


F = Farmer("Bill", 1000)
F.months = 15

print(F.money())

执行结果

1
15000

多态

同一个变量在调用同一个方法时,完全可能呈现出多种行为(具体呈现出哪种行为由该变量所引用的对象来决定),这就是所谓的多态(Polymorphism)

创建同样的对象和调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Film:
def go(self, name):
print("我们明天去看电影 %s 如何?" % name)


class Action:
def go(self, name):
print("我们明天去 %s 如何?" % name)


m = Film()
m.go("《白蛇.缘起》")

m = Action()
m.go("打羽毛球")

执行结果

1
2
我们明天去看电影 《白蛇.缘起》 如何?
我们明天去 打羽毛球 如何?

传参数为函数(实际应用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Activity:

def action(self, action_name):
print("--- 周末怎么安排? ---")
action_name.go(self)


class Run:
def go(self, name):
print("我们去 %s 跑步怎么样?" % name)


class Basketball:
def go(self, name):
print("我们去 %s 打篮球怎么样?" % name)


class PingPong:
def go(self, name):
print("我们去 %s 打打乒乓球怎么样?" % name)


m = Activity()
m.action(Run())
m.action(Basketball())
m.action(PingPong())

执行结果

1
2
3
4
5
6
--- 周末怎么安排? ---
我们去 <__main__.Activity object at 0x00000196AF5D1128> 跑步怎么样?
--- 周末怎么安排? ---
我们去 <__main__.Activity object at 0x00000196AF5D1128> 打篮球怎么样?
--- 周末怎么安排? ---
我们去 <__main__.Activity object at 0x00000196AF5D1128> 打打乒乓球怎么样?

issubclass和isinstance检查类型

  • issubclass(cls, class_or_tuple):检查 cls 是否为后一个类或元组包含的多个类中任意类的子类。
  • isinstance(obj, class_or_tuple):检查 obj 是否为后一个类或元组包含的多个类中任意类的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 字符串
name = "Farmer"

# 实例
print('"Name"是否是str类的实例: ', isinstance(name, str))
print('"Name"是否是object类的实例: ', isinstance(name, object))
print('"Name"是否是tuple类的实例: ', isinstance(name, tuple))

# 子类
print('str是否是object类的子类: ', issubclass(str, object))
print('str是否是tuple类的子类: ', issubclass(str, tuple))

# 列表
name_list = [2, 4]

# 实例
print('列表[2, 4]是否是list类的实例: ', isinstance(name_list, list))
print('列表[2, 4]是否是object类及其子类的实例: ', isinstance(name_list, object))
print('列表[2, 4]是否是tuple类及其子类的实例: ', isinstance([2, 4], tuple))

# 子类
print('list是否是object类的子类: ', issubclass(list, object))
print('list是否是tuple类的子类: ', issubclass(list, tuple))

执行结果

1
2
3
4
5
6
7
8
9
10
"Name"是否是str类的实例:  True
"Name"是否是object类的实例: True
"Name"是否是tuple类的实例: False
str是否是object类的子类: True
str是否是tuple类的子类: False
列表[2, 4]是否是list类的实例: True
列表[2, 4]是否是object类及其子类的实例: True
列表[2, 4]是否是tuple类及其子类的实例: False
list是否是object类的子类: True
list是否是tuple类的子类: False

枚举类

使用普通类直接实现枚举

在Python中,枚举和我们在对象中定义的类变量时一样的,每一个类变量就是一个枚举项,访问枚举项的方式为:类名加上类变量, 如下:

1
2
3
4
5
6
7
8
class Color:
YELLOW = 1
RED = 2
GREEN = 3
PINK = 4


print(Color.GREEN)

执行结果

1
3

这种可以解决一些问题,但是不严谨,因为枚举类中不应该存在key相同的枚举项,也不准类外修改枚举项的值

enum模块

enum模块是系统内置模块,可以直接使用import导入,但是在导入的时候,不建议使用import enum将enum模块中的所有数据都导入,一般使用的最多的就是enum模块中的Enum、IntEnum、unique这几项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from enum import Enum


class Location(Enum):
SOUTH = 1
WEST = 2
EAST = 3
NORTH = 4

def info(self):
print("这是方向 %s 的枚举" % self.value)


print(Location.WEST)
print(Location.WEST.value)
print(Location['EAST'])

Location.NORTH.info()

for i in Location:
print("方向名字为 %s, 值为 %d" %(i.name, i.value))

执行结果

1
2
3
4
5
6
7
8
Location.WEST
2
Location.EAST
这是方向 4 的枚举
方向名字为 SOUTH, 值为 1
方向名字为 WEST, 值为 2
方向名字为 EAST, 值为 3
方向名字为 NORTH, 值为 4

异常处理机制

Python 的异常机制主要依赖 try 、except 、else、finally 和 raise 五个关键字:

  • try 关键字后缩进的代码块简称 try 块,它里面放置的是可能引发异常的代码
  • 在 except 后对应的是异常类型和一个代码块,用于表明该 except 块处理这种类型的代码块
  • 在多个 except 块之后可以放一个 else 块,表明程序不出现异常时还要执行 else 块
  • finally 块用于回收在 try 块里打开的物理资源,异常机制会保证 f- inally 块总被执行
  • raise 用于引发一个实际的异常,raise 可以单独作为语句使用,引发一个具体的异常对象

try except else(异常处理)

try:执行可能会出错的试探性语句,即这里面的语句是可以导致致命性错误使得程序无法继续执行下去

except:如果try里面的语句无法正确执行,那么就执行except里面的语句,这里面可以是错误信息或者其他的可执行语句

else:如果try里面的语句可以正常执行,那么就执行else里面的语句(相当于程序没有碰到致命性错误)

try…except捕获异常

异常处理机制的语法结果

1
2
3
4
5
6
7
try:
# 业务代码
...
except (Error1, Error2, ...) as e:
# do something
else:
# do something

如果在执行 try 块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给 Python 解释器,这个过程被称为引发异常。

当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为捕获异常。如果 Python 解释器找不到捕获异常的 except 块,则运行时环境终止,Python 解释器也将退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
num = input("请输入您要求和的数字, 格式为a,b: \n")

while num is not None:
try:
a, b = num.split(',')
if int(a) + int(b) > 9999:
num = input("您输入的数字和值超过最大9999, 请重新输入: \n")
continue
else:
print("您输入的数字的和值为: ", int(a) + int(b))
num = input("请输入您要求和的数字, 格式为a,b: \n")
continue
except Exception as e:
num = input("您输入的数字不合法, 请重新输入: \n")
continue

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
请输入您要求和的数字, 格式为a,b: 
8,9
您输入的数字的和值为: 17
请输入您要求和的数字, 格式为a,b:
8,9
您输入的数字的和值为: 17
请输入您要求和的数字, 格式为a,b:
a,b
您输入的数字不合法, 请重新输入:
1000,8000
您输入的数字的和值为: 9000
请输入您要求和的数字, 格式为a,b:
2000,8000
您输入的数字和值超过最大9999, 请重新输入:
20,177
您输入的数字的和值为: 197
请输入您要求和的数字, 格式为a,b:

finally资源回收

不管 try 块中的代码是否出现异常,也不管哪一个 except 块被执行,甚至在 try 块或 except 块中执行了 return 语句,finally 块总会被执行。

语法结构如下

1
2
3
4
5
6
7
8
9
10
11
12
try:
# 业务实现代码
except SubException as e:
# 异常处理块1
...
except SubException2 as e:
# 异常处理块2
...
else:
# 正常处理块
finally :
# 资源回收块

raise

系统是否要引发异常,可能需要根据应用的业务需求来决定,如果程序中的数据、执行与既定的业务需求不符,这就是一种异常。由于与业务需求不符而产生的异常,必须由程序员来决定引发,系统无法引发这种异常。

如果需要在程序中自行引发异常,则应使用 raise 语句。raise 语句有如下三种常用的用法:
raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
raise 异常类:raise 后带一个异常类。该语句引发指定异常类的默认实例。
raise 异常对象:引发指定的异常对象。(可以只使用raise不带参数,默认RuntimeError )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
num = input("请输入您要求和的数字, 格式为a,b: \n")

while num is not None:
try:
a, b = num.split(',')
if int(a) + int(b) > 9999:
# 手动抛出异常
raise ValueError # 这里可以只使用raise不带参数,默认RuntimeError
else:
print("您输入的数字的和值为: ", int(a) + int(b))
num = input("请输入您要求和的数字, 格式为a,b: \n")
continue
except Exception as e:
num = input("您输入的数字不合法, 请重新输入: \n")
continue

执行结果

1
2
3
4
5
6
请输入您要求和的数字, 格式为a,b: 
2000,8000
您输入的数字不合法, 请重新输入:
2,8
您输入的数字的和值为: 10
请输入您要求和的数字, 格式为a,b:

自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 自定义异常
class CustomError(Exception):
pass


num = input("请输入您要求和的数字, 格式为a,b: \n")

while num is not None:
try:
a, b = num.split(',')
if int(a) + int(b) > 9999:
# 引用自定义的异常
raise CustomError
else:
print("您输入的数字的和值为: ", int(a) + int(b))
num = input("请输入您要求和的数字, 格式为a,b: \n")
continue
except Exception as e:
num = input("您输入的数字不合法, 请重新输入: \n")
continue

执行结果

1
2
3
请输入您要求和的数字, 格式为a,b: 
2000,9000
您输入的数字不合法, 请重新输入:

类的特殊成员

在 Python 类中有些方法名、属性名的前后都添加了双下画线,这种方法、属性通常都属于 Python 的特殊方法和特殊属性
常见如__init__,通过重新这个类,可以实现自己的初始化

__repr__()显示属性

__repr__() 是一个非常特殊的方法,它是一个“自我描述”的方法,该方法通常用于实现这样一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用来告诉外界该对象具有的状态信息。

1
2
3
4
5
6
7
8
9
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age


h = Hello("Farmer", 20)
print(h)
print(h.__repr__())

执行结果

1
2
<__main__.Hello object at 0x000002044BCC0780>
<__main__.Hello object at 0x000002044BCC0780>

__repr__() 是 Python 类中的一个特殊方法,由于 object 类己提供了该方法,而所有的 Python 类都是 object 类的子类,因此所有的 Python 对象都具有 __repr__() 方法。

重写repr

改写如下

1
2
3
4
5
6
7
8
9
10
11
12
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def __repr__(self):
return "Name is " + self.name + " Age is " + str(self.age)


h = Hello("Farmer", 20)
print(h)
print(h.__repr__())

执行结果

1
2
Name is Farmer Age is 20
Name is Farmer Age is 20

__del__ 删除对象

与 init() 方法对应的是 __del__() 方法,__init__() 方法用于初始化 Python 对象,而 __del__() 则用于销毁 Python 对象,即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的 __del__() 方法

当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC,Garbage Collector),Python 会自动回收所有对象所占用的内存空间,因此开发者无须关心对象垃圾回收的过程。

Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def __del__(self):
print("删除Hello对象")


h = Hello("Farmer", 20)

a = h
del h
# 这个输出在前,因为h此时还有被a引用,需要等到程序结束后引用变为0执行删除
print("-------------")

执行结果

1
2
-------------
删除Hello对象

如果注释掉 a = h,结果如下

1
2
删除Hello对象
-------------

此时删除后系统会立即回收对象,无需等到程序结束,因为此时程序中没有任何变量在使用这个对象

__dir__列出对象的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def miss(self):
pass


h = Hello("Farmer", 20)
# 返回对象属性的列表
print(h.__dir__())
# 返回对象属性的排列后列表
print(dir(h))

执行结果

1
2
3
['name', 'age', '__module__', '__init__', 'miss', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'miss', 'name']

__dict__对象内部所有属性名和属性值组成的字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def miss(self):
pass


h = Hello("Farmer", 20)
# 获取dict
print(h.__dict__)
# 通过dict获取name的值
print(h.__dict__["name"])
# 直接获取name的值
print(h.name)

执行结果

1
2
3
{'name': 'Farmer', 'age': 20}
Farmer
Farmer

__call__方法

__call__用来确定是否可调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def miss(self):
pass


h = Hello("Farmer", 20)
# 查看是否有name属性
print("Hello 是否有name属性: ", hasattr(h, "name"))
# 查看name是否可以调用
print("name是否可以调用: ", hasattr(h.name, "__call__"))
# 查看miss是否可以调用
print("miss是否可以调用: ", hasattr(h.miss, "__call__"))

执行结果

1
2
3
Hello 是否有name属性:  True
name是否可以调用: False
miss是否可以调用: True

__call__调用函数和用函数名后加()调用

直接函数名加上()调用其实就是改对象的__call__调用

1
2
3
4
5
6
7
8
9
10
11
12
13
class Hello:
def __init__(self, name, age):
self.name = name
self.age = age

def miss(self):
print("我的名字叫: ", self.name, "\n我今年 ", self.age, " 岁")


h = Hello("Farmer", 20)
h.miss.__call__()
print("--------------------------------")
h.miss()

执行结果

1
2
3
4
5
我的名字叫:  Farmer 
我今年 20 岁
--------------------------------
我的名字叫: Farmer
我今年 20 岁

h.miss.__call__()和h.miss() 执行结果一样

序列

序列最重要的特征就是可包含多个元素, 因此和序列有关的特殊方法有如下几个:

  • __len__(self):该方法的返回值决定序列中元素的个数。
  • __getitem__(self, key):该方法获取指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。
  • __contains__(self, item):该方法判断序列是否包含指定元素。
  • __setitem__(self, key, value):该方法设置指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。
  • __delitem__(self, key):该方法删除指定索引对应的元素。

__len__()

如果一个类表现得像一个list,要获取有多少个元素,需要用len()

1
2
3
4
5
6
7
8
9
10
class Lang:
def __init__(self, *args):
self.name = args

def __len__(self):
return len(self.name)


l = Lang("C", "Go", "Java", "Python", "C++")
print(len(l))

执行结果

1
5

__getitem__(self, key)

可以让对象实现迭代功能,这样就可以使用for…in… 来迭代该对象

1
2
3
4
5
6
7
8
9
10
11
12
class Lang:
def __init__(self, lang_list):
self.name = lang_list

def __getitem__(self, index):
return self.name[index]


l = Lang(["C", "Go", "Java", "Python", "C++"])

for i in l:
print(i)

执行结果

1
2
3
4
5
C
Go
Java
Python
C++

__contains__(self, item)

Python 2 中字典(Dictionary) has_key() 函数用于判断键是否存在于字典中,如果键在字典dict里返回true,否则返回false
Python 3 中字典(Dictionary) has_key() 函数变为 contains(key),用法一样

1
2
3
di = {"name": "farmer", "age": 20}
print(di.__contains__("name"))
print(di.__contains__("home"))

执行结果

1
2
True
False

__setitem__(self, key, value)

该方法应该按一定的方式存储和key相关的value。在设置类实例属性时自动调用的

1
2
3
4
5
6
7
8
9
10
11
class T:
def __init__(self, name, home):
self['name'] = name
self['home'] = home

def __setitem__(self, key, value):
print("新设置的Key为: %s, 设置的值为: %s" % (key, value))


M = T("Farmer", "BJ")
M1 = T("Lucy", "SH")

执行结果

1
2
3
4
新设置的Key为: name, 设置的值为: Farmer
新设置的Key为: home, 设置的值为: BJ
新设置的Key为: name, 设置的值为: Lucy
新设置的Key为: home, 设置的值为: SH

__delitem__(self, key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class T:
def __init__(self):
self.change = {"name": "Farmer", "home": "BJ"}

def __getitem__(self, item):
print('调用getitem')
return self.change[item]

def __setitem__(self, key, value):
print('调用setitem')
self.change[key] = value

def __delitem__(self, key):
print("执行删除操作,调用delitem")
del self.change[key]


M = T()
print("--------------first line----------------------")
print("打印name的value: ", M['name'])
print("--------------second line----------------------")
M['age'] = 20
print("--------------third line----------------------")
print("打印age的value: ", M['age'])
print("--------------fourth line----------------------")
del M['name']
print("--------------fifth line----------------------")
print(M.change)

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
--------------first line----------------------
调用getitem
打印name的value: Farmer
--------------second line----------------------
调用setitem
--------------third line----------------------
调用getitem
打印age的value: 20
--------------fourth line----------------------
执行删除操作,调用delitem
--------------fifth line----------------------
{'home': 'BJ', 'age': 20}

迭代器

如果开发者需要实现法代器,只要实现如下两个方法即可:

  • __iter__(self):该方法返回一个法代器(iterator),迭代器必须包含一个__next__()方法,该方法返回迭代器的下一个元素。
  • __reversed__(self):该方法主要为内建的reversed()反转函数提供支持,当程序调用 reversed() 函数对指定迭代器执行反转时,实际上是由该方法实现的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class T:
def __init__(self):
self.x = 10
self.y = 0

def __next__(self):
if self.x <= 1:
raise StopIteration

self.x -= 1
self.y += 1
z = self.y * self.x
return "%d*%d=%2d" % (self.y, self.x, z)

def __iter__(self):
return self


M = T()
print("---------------")
print(next(M))
print("---------------")
for i in M:
print(i)

执行结果

1
2
3
4
5
6
7
8
9
10
11
---------------
1*9= 9
---------------
2*8=16
3*7=21
4*6=24
5*5=25
6*4=24
7*3=21
8*2=16
9*1= 9

生成器

生成器和法代器的功能非常相似,它也会提供 __next__() 方法,这意味着程序同样可调用内置的 next() 函数来获取生成器的下一个值,也可使用 for 循环来遍历生成器。

生成器(generator)能够迭代的关键是它有一个next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。

带有 yield 的函数不再是一个普通函数,而是一个生成器generator。可用next()调用生成器对象来取值。next 两种方式 t.__next__() | next(t)。可用for 循环获取返回值(每执行一次,取生成器里面一个值),(基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。

yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行

.send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果

创建生成器需要两步操作:

  • 定义一个包含 yield 语句的函数。
  • 调用第 1 步创建的函数得到生成器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def t():
i = 0
while i <= 5:
tt = yield i
print(tt)
i += 1


m = t()
print("------------------------")
m.__next__()
print("------------------------")
m.send("Hello")
print("------------------------")
for n in m:
print(n)

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
------------------------
------------------------
Hello
------------------------
None
2
None
3
None
4
None
5
None

因为yield 只有到 next之后才会执行下面的,而temp 停留在yield i, 没有被赋值到,send才会被赋值到

yield 实现斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fib(num):
x, y, count = 0, 1, 0
while count <= num:
yield x
x, y = y, x+y
count += 1


f = fib(10)
print("-------------------------")
print(f.__next__())
print("-------------------------")
for i in f:
print(i, end=" ")

执行结果

1
2
3
4
-------------------------
0
-------------------------
1 1 2 3 5 8 13 21 34 55

modules 和 packages

导入模块(import)

import 用法如下

  • import modules as alias_name # 设置别名
  • import modules # 导入整个模块
  • from modules import XX # 从模块中导入指定的成员
  • from modules import XX as alias_name # 从模块中导入指定的成员并设置别名
1
2
import sys as s
from os import open

自定义模块

模块就是 Python 程序。任何 Python 程序都可作为模块导入。对于任何程序,只要导入了模块,即可使用该模块内的所有成员。

1
2
3
4
5
6
7
8
9
10
11
def hello(name):
print("你好 ", name)


class H:
def __init__(self, name, age):
self.name = name
self.age = age

def user(self):
print("你输入的用户名称是: %s, 年龄是: %d" % (self.name, self.age))

文件名字test.py

可以直接import test.py as t 或者from test.py import XX

增加说明文档

只需在模块开始增加一个字符串的说明即开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""
这是自定义的模块:
hello: 简单的问候语函数
H: 输入用户的类
"""


def hello(name):
print("你好 ", name)


class H:
def __init__(self, name, age):
self.name = name
self.age = age

def user(self):
print("你输入的用户名称是: %s, 年龄是: %d" % (self.name, self.age))


# 可以输出说明文档
print(__doc__)

执行结果

1
2
3
这是自定义的模块:
hello: 简单的问候语函数
H: 输入用户的类

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"""
这是自定义的模块:
hello: 简单的问候语函数
H: 输入用户的类
"""


def hello(name):
print("你好 ", name)


class H:
def __init__(self, name, age):
self.name = name
self.age = age

def user(self):
print("你输入的用户名称是: %s, 年龄是: %d" % (self.name, self.age))


def test_hello():
hello("Lucy")


def test_h():
t = H("Farmer", 20)
t.user()


# 其他程序调用该模块时不会执行,单独执行本模块会执行
if __name__ == "__main__":
test_hello()
test_h()

单独执行改模块会得到如下结果

1
2
你好  Lucy
你输入的用户名称是: Farmer, 年龄是: 20

如果只是简单地调用上面的测试程序,则会导致一个问题,那就是当其他程序每次导入该模块时,这三个测试函数都会自动运行,这显然不是我们期望看到的结果。此时希望实现的效果是,如果直接使用 python 命令运行该模块(相当于测试),程序应该执行该模块的测试函数;如果是其他程序导入该模块,程序不应该执行该模块的测试函数。

此时可借助于所有模块内置的 __name__ 变量进行区分,如果直接使用 python 命令来运行一个模块,name 变量的值为 __main__;如果该模块被导入其他程序中,__name__ 变量的值就是模块名。因此,如果希望测试函数只有在使用 python 命令直接运行时才执行,则可在调用测试函数时增加判断,只有当 __name__ 属性为 __main__ 时才调用测试函数。

指定默认导入的模块

在默认情况下,如果使用“from 模块名 import *”这样的语句来导入模块,程序会导入该模块中所有不以下画线开头的程序单元, 但是有时候我们不希望每个成员都被暴露出来使用,这里需要用到__all__

模块名字all.py,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
def a(name):
print("你好 ", name)


def b(name):
print("我的名字叫 ", name)


def c(name):
print("他的名字叫 ", name)


__all__ = ["a", "b"]

调用如下

1
2
3
4
5
from all import *

a("farmer")
b("lucy")
c("Bill") # 这个调用会报错

执行结果如下

1
2
3
4
5
6
你好  farmer
我的名字叫 lucy
Traceback (most recent call last):
File "D:/python工程/ttt.py", line 8, in <module>
c("Bill")
NameError: name 'c' is not defined

packages

packages 定义

为了组织好模块,会将多个模块分为包。Python 处理包也是相当方便的。简单来说,包就是文件夹,但该文件夹下必须存在__init__.py 文件,该文件可为空。

常见的包结构

1
2
3
4
5
packages
|------- __init__.py
|------- module1.py
|------- modules2.py
|------- ...

导入包

包的导入仍使用 import 、 from … import 语句,使用 “圆点模块名” 的结构化模块命名空间

如果导入的包不存在会引发ImportError异常

1
2
3
4
5
6
7
8
9
10
11
test
|------- __init__.py
|------- add
|-----------__init__.py
|-----------a.py
|-----------b.py
|------- power
|-----------__init__.py
|-----------p1.py
|-----------p2.py
|------- ...

导入引用

1
2
3
4
# 导入test的add下a子模块
import test.add.a
# 或者
from test.power import p1

包内引用

如果是子包内的引用,可以按相对位置引入子模块

上述a为例

1
from ..power import p1

查看模块的源文件路径

__file__属性可以查看指定模块的源文件路径

1
2
3
4
>>> import string
>>> string.__file__
'/usr/local/python3/lib/python3.7/string.py'
>>>

Python Tkinter GUI编程

GUI编程组件及用法

使用tk创建窗口

1
2
3
4
5
6
7
8
9
10
11
from tkinter import *


root = Tk()
# 设置窗口标题
root.title("我的GUI")
w = Label(root, text="你好,陌生人")
# 调用pack进行布局
w.pack()
# 启动主窗口的消息循环,不然窗口看不到
root.mainloop()

执行结果如下

通过frame子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding:utf-8 -*-
# __author: "Farmer"
# date: 2018/6/14
from tkinter import *


class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.initWidgets()

def initWidgets(self):
# 创建label对象
w = Label(self)

# 位图, 注意这里不要用jpg的图片
bgp = PhotoImage(file='bgp.png')

# 用一个不会被释放的变量引用该图片,否则该图片会被回收
w.x = bgp

# 显示的图片
w['image'] = bgp
w.pack()

# 创建Button对象,第一个参数指定该Button放入root, Button的文本,Button的颜色
confirm_button = Button(self, text="确定", background="white")
confirm_button.pack()


m = App()
print(type(m.master))

m.master.title("白蛇.缘起")
m.mainloop()

执行结果

上面程序只是创建了 Application 的实例(Frame 容器的子类),并未创建 Tk 对象(窗口),那么这个程序有窗口吗?答案是肯定的。如果程序在创建任意 Widget 组件(甚至 Button)时没有指定 master 属性(即创建 Widget 组件时第一个参数传入 None),那么程序会自动为该 Widget 组件创建一个 Tk 窗口,因此 Python 会自动为 Application 实例创建 Tk 对象来作为它的 master。

GUI组件支持的通用选项

选项名(别名)含义单位典型值
activebackground指定组件处于激活状态时的背景色color‘gray25’或’#ff4400’
activeforeground指定组件处于激活状态时的前景色color‘gray25’或’#ff4400’
anchor指定组件内的信息(比如文本或图片)在组件中如何显示。必须为下面的值之一:N、NE、E、SE、S、SW、W、NW或CENTER。比如NW(NorthWest)指定将信息显示在组件的左上角CENTER
background(bg)指定组件正常显示时的背景色color‘gray25’或’#ff4400’
bitmap指定在组件上显示该选项指定的位图,该选项值可以是Tk_GetBitmap接收的任何形式的位图。位图的显示方式受anchor、justify选项的影响。如果同时指定了bitmap和text,那么bitmap 覆盖文本;如果同时指定了bitmap 和image,那么image 覆盖bitmap
borderwidth指定组件正常显示时的3D边框的宽度,该值可以是Tk_GetPixels接收的任何格式pixel2
cursor指定光标在组件上的样式。该值可以是Tk_GetCursors 接受的任何格式cursorgumby
command指定按组件关联的命令方法,该方法通常在鼠标离开组件时被触发调用
disabledforeground指定组件处于禁用状态时的前景色color‘gray25’或’#ff4400’
font指定组件上显示的文本字体font‘Helvetica’或(‘Verdana’, 8)
foreground(fg)指定组件正常显示时的前景色color‘gray’或’#ff4400’
highlightbackground指定组件在高亮状态下的背景色color‘gray’或’#ff4400’
highlightcolor指定组件在高亮状态下的前景色color‘gray’或’#ff4400’
highlightthickness指定组件在高亮状态下的周围方形区域的宽度,该值可以是Tk_GetPixels接收的任何格式pixel2
height指定组件的高度,以font选项指定的字体的字符高度为单位,至少为1integer14
image指定组件中显示的图像,如果设置了image 选项,它将会覆盖text、bitmap选项image
justify指定组件内部内容的对齐方式,该选项支持LEFT(左对齐)、CENTER(居中对齐)或RIGHT(右对齐)这三个值constantRIGHT
padx指定组件内部在水平方向上两边的空白,该值可以是Tk_GctPixels接收的任何格式pixel12
pady指定组件内部在垂直方向上两地的空白,该值可以是Tk_GctPixels接收的任何格式pixel12
relief指定组件的3D 效果,该选项支持的值包括RAISED、SUNKEN、FLAT、RIDGE、SOLID、GROOVE。该值指出组件内部相对于外部的外观样式,比如RAISED表示组件内部相对于外部凸起constantGROOVE RAISED
selectbackground指定组件在选中状态下的背景色color‘gray’或’#ff4400’
selectborderwidth指定组件在选中状态下的3D边框的宽度,该值可以是Tk_GetPixels接收的任何格式pixel2
selectforeground指定组在选中状态下的前景色color‘gray’或’#ff4400’
state指定组件的当前状态。该选项支持NORMAL(正常)、DISABLE(禁用)这两个值constantNORMAL
takefocus指定组件在键盘遍历(Tab 或 Shift+Tab)时是否接收焦点,将该选项设为 1 表示接收焦点;设为 0 表示不接收焦点boolean1或YES
text指定组件上显示的文本,文本显示格式由组件本身、anchor 及 justify 选项决定str‘确定’
textvariable指定一个变量名,GUI 组件负责显示该变量值转换得到的字符串,文本显示格式由组件本身、anchor 及 justify 选项决定variablebnText
underline指定为组件文本的第几个字符添加下画线,该选项就相当于为组件绑定了快捷键integer2
width指定组件的宽度,以font 选项指定的字体的字符高度为单位,至少为 1integer14
wraplength对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超过该数量的字符将会转到下行显示integer20
xscrollcommand通常用于将组件的水平滚动改变(包括内容滚动或宽度发生改变)与水平滚动条的set方法关联,从而让组件的水平滚动改变传递到水平滚动条functionscroll.set
yscrollcommand通常用于将组件的垂直滚动改变(包括内容滚动或高度发生改变)与垂直滚动条的set 方法关联,从而让组件的垂直滚动改变传递到垂直滚动条functionscroll.set

Pack布局管理

pack() 方法通常可支持如下选项:

  • anchor:当可用空间大于组件所需求的大小时,该选项决定组件被放置在容器的何处。该选项支持 N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左上)、NE(东北,代表右上)、SW(西南,代表左下)、SE(东南,代表右下)、CENTER(中,默认值)这些值。
  • expand:该 bool 值指定当父容器增大时才是否拉伸组件。
  • fill:设置组件是否沿水平或垂直方向填充。该选项支持 NONE、X、Y、BOTH 四个值,其中 – NONE 表示不填充,BOTH 表示沿着两个方向填充。
  • ipadx:指定组件在 x 方向(水平)上的内部留白(padding)。
  • ipady:指定组件在 y 方向(水平)上的内部留白(padding)。
  • padx:指定组件在 x 方向(水平)上与其他组件的间距。
  • pady:指定组件在 y 方向(水平)上与其他组件的间距。
  • side:设置组件的添加位置,可以设置为 TOP、BOTTOM、LEFT 或 RIGHT 这四个值的其中之一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from tkinter import *


class App(Frame):
def __init__(self, master=None):
# self.master = master
Frame.__init__(self, master)
self.pack()
self.initWidgets()

def initWidgets(self):
# 创建第一个容器
fm1 = Label(self, text="LEFT", bg="red")
fm1.pack(side=LEFT, fill=BOTH, expand=YES)
# 创建第二个容器
fm2 = Label(self, text="TOP", bg="green")
fm2.pack(side=TOP, fill=BOTH, expand=YES)
# 创建第三个容器
fm3 = Label(self, text="RIGHT", bg="yellow")
fm3.pack(side=RIGHT, fill=BOTH, expand=YES)
# 创建第四个容器
fm4 = Label(self, text="BOTTOM", bg="blue")
fm4.pack(side=BOTTOM, fill=BOTH, expand=YES)


root = Tk()
root.title("Pack布局")
root.geometry('800x600')
display = App(root)
root.mainloop()

执行结果

Grid布局

Grid 把组件空间分解成一个网格进行维护,即按照行、列的方式排列组件,组件位置由其所在的行号和列号决定,行号相同而列号不同的几个组件会被依次上下排列,列号相同而行号不同的几个组件会被依次左右排列。

程序调用组件的 grid() 方法就进行 Grid 布局,在调用 grid() 方法时可传入多个选项,该方法支持的 ipadx、ipady、padx、pady 与 pack() 方法的这些选项相同。而 grid() 方法额外增加了如下选项:

  • column:指定将组件放入哪列。第一列的索引为 0。
  • columnspan:指定组件横跨多少列。
  • row:指定组件放入哪行。第一行的索引为 0
  • rowspan:指定组件横跨多少行。
  • sticky:有点类似于 pack() 方法的 anchor 选项,同样支持 N(北,代表上)、E(东,代表右)、S(南,代表下)、W(西,代表左)、NW(西北,代表左上)、NE(东北,代表右上)、SW(西南,代表左下)、SE(东南,代表右下)、CENTER(中,默认值)这些值。

使用grid进行布局管理非常容易。只需要创建控件,然后使用grid方法去告诉布局管理器在合适的行和列去显示它们。你不用事先指定每个网格的大小,布局管理器会自动根据里面的控件进行调节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from tkinter import *
master = Tk()
Label(master, text="First").grid(row=0)
Label(master, text="Second").grid(row=1)


e1 = Entry(master)
e2 = Entry(master)


e1.grid(row=0, column=1)
e2.grid(row=1, column=1)

mainloop()

运行如下:

label1占据0行0列,label2占据1行0列,entry1占据0行1列,entry2占据1行1列。

注意:

  • 在使用grid方法时,如果不指定column参数,则默认从0开始。
  • 没有被使用的行和列号将被忽略

使用sticky参数

默认的空间会在网格中居中显示。你可以使用sticky选项去指定对齐方式,可以选择的值有:N/S/E/W,分别代表上/下/左/右。如果你想让label靠左显示,你可以设置stricky的值为W。

可以指定控件跨越一个或者多个网格。columnspan选项可以指定控件跨越多列显示,而rowspan选项同样可以指定控件跨越多行显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from tkinter import *


master = Tk()
var = IntVar()

Label(master, text="First").grid(sticky=E)
Label(master, text="Second").grid(sticky=E)

e1 = Entry(master)
e2 = Entry(master)

e1.grid(row=0, column=1)
e2.grid(row=1, column=1)

checkbutton = Checkbutton(master, text='Preserve aspect', variable=var)
checkbutton.grid(columnspan=2, sticky=W)

photo = PhotoImage(file='timg.png')
label = Label(image=photo)
label.image = photo
label.grid(row=0, column=2, columnspan=2, rowspan=2, sticky=W+E+N+S, padx=5, pady=5)

button1 = Button(master, text='Zoom in')
button1.grid(row=2, column=2)

button2 = Button(master, text='Zoom out')
button2.grid(row=2, column=3)

mainloop()

运行如下

在这段代码中,有一些细节需要注意:

  • 我们没有为左边的两个label控件指定具体的位置,在这种情况下,column将会从0开始,而row将会从第一个没有使用的值开始。
  • 我们队checkbutton设置了columnspan参数,所以它会显示在第二行,并占据第0和1列。
  • 图像label占用了2行2列,而最后的两个button都只占用了1列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from tkinter import *


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建一个输入组件
e = Entry(relief=SUNKEN, font=('Courier New', 24), width=25)
# 对该输入组件使用Pack布局,放在容器顶部
e.pack(side=TOP, pady=10)
p = Frame(self.master)
p.pack(side=TOP)
# 定义字符串的元组
names = ("0", "1", "2", "3"
, "4", "5", "6", "7", "8", "9"
, "+", "-", "*", "/", ".", "=")
# 遍历字符串元组
for i in range(len(names)):
# 创建Button,将Button放入p组件中
b = Button(p, text=names[i], font=('Verdana', 20), width=6)
b.grid(row=i // 4, column=i % 4)


root = Tk()
root.title("Grid布局")
App(root)
root.mainloop()

运行

Place布局

Place 布局就是其他 GUI 编程中的“绝对布局”,这种布局方式要求程序显式指定每个组件的绝对位置或相对于其他组件的位置。

如果要使用 Place 布局,调用相应组件的 place() 方法即可。在使用该方法时同样支持一些详细的选项,关于这些选项的介绍如下:

  • x:指定组件的 X 坐标。x 为 0 代表位于最左边。
  • y:指定组件的 Y 坐标。y 为 0 代表位于最右边。
  • relx:指定组件的 X 坐标,以父容器总宽度为单位 1,该值应该在 0.0~1.0 之间,其中 0.0 代表位于窗口最左边,1.0 代表位于窗口最右边,0.5 代表位于窗口中间。
  • rely:指定组件的 Y 坐标,以父容器总高度为单位 1,该值应该在 0.0~1.0 之间,其中 0.0 代表位于窗口最上边,1.0 代表位于窗口最下边,0.5 代表位于窗口中间。
  • width:指定组件的宽度,以 pixel 为单位。
  • height:指定组件的高度,以 pixel 为单位。
  • relwidth:指定组件的宽度,以父容器总宽度为单位 1,该值应该在 0.0~1.0 之间,其中 1.0 代表整个窗口宽度,0.5 代表窗口的一半宽度。
  • relheight:指定组件的高度,以父容器总高度为单位 1,该值应该在 0.0~1.0 之间,其中 1.0 代表整个窗口高度,0.5 代表窗口的一半高度。
  • bordermode:该属性支持“inside”或“outside” 属性值,用于指定当设置组件的宽度、高度时是否计算该组件的边框宽度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from tkinter import *


class A:

def __init__(self, master=None):
self.root = master
self.root.geometry('800x600+200+200')
self.root.title('测试')
# self.root.bind("<Motion>", self.call_back)
self.frm1 = Frame(self.root)
self.frm2 = Frame(self.root)
self.frm3 = Frame(self.root)
self.createpage()

def call_back(self, event):
print('现在的位置是:', event.x_root, event.y_root)

def createpage(self):
menu = Menu(self.root)
self.root.config(menu=menu)

filemenu = Menu(menu)
menu.add_cascade(label='测试1', menu=filemenu)
filemenu.add_command(label='1')
filemenu.add_command(label='2')
filemenu.add_command(label='3')

onemenu = Menu(menu)
menu.add_cascade(label='测试2', menu=onemenu)
onemenu.add_command(label='1')
onemenu.add_command(label='2')
onemenu.add_command(label='3')

self.frm1.config(bg='blue', height=500, width=600)
Label(self.frm1, text='frm1').place(in_=self.frm1, anchor=NW)
self.frm1.place(x=180, y=50)

self.frm2.config(bg='red', height=500, width=150)
Label(self.frm2, text='frm2').place(anchor=NW)
self.frm2.place(x=20, y=50)

self.frm3.config(bg='yellow', height=40, width=760)
Label(self.frm3, text='frm3').place(in_=self.frm3, anchor=NW)
self.frm3.place(x=20, y=5)

# frm3下的Label
Label(self.frm3, text='Label Test Test',
fg='red', font='Verdana 10 bold').place(x=300, y=10)
# frm2下的Button
for i in range(9):
Button(self.frm2, text='Button%d' % i).place(x=20, y=20 + i * 50, width=100)

# frm1下的控件
Label(self.frm1, text='项目资源管理平台',
fg='red', font='Verdana 10 bold').place(x=100, y=50, height=80, width=400)
Button(self.frm1, text='1', height=1, width=1).place(x=450, y=450)
Button(self.frm1, text='2', height=1, width=1).place(x=490, y=450)
Button(self.frm1, text='3', height=1, width=1).place(x=530, y=450)


if __name__ == '__main__':
root = Tk()
A(root)
mainloop()

运行结果

command和bind事件处理

command 绑定事件处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from tkinter import *

import random


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.label = Label(self.master, width=30)
self.label['font'] = ('Courier', 20)
self.label['bg'] = 'white'
self.label.pack()
bn = Button(self.master, text='单击我', command=self.change)
bn.pack()

# 定义事件处理方法
def change(self):
self.label['text'] = '欢迎光临'
# 生成3个随机数
ct = [random.randrange(256) for x in range(3)]
grayness = int(round(0.299 * ct[0] + 0.587 * ct[1] + 0.114 * ct[2]))
# 将元组中3个随机数格式化成16进制数,转成颜色格式
bg_color = "#%02x%02x%02x" % tuple(ct)
self.label['bg'] = bg_color
self.label['fg'] = 'black' if grayness > 125 else 'white'


root = Tk()
root.title("简单事件处理")
App(root)
root.mainloop()

运行如下

tk00011

bind绑定事件处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from tkinter import *
import sys


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.show = Label(self.master, width=30, bg='white', font=('times', 20))
self.show.pack()
bn = Button(self.master, text='单击我或双击我')
bn.pack(fill=BOTH, expand=YES)
# 为左键单击事件绑定处理方法
bn.bind('<Button-1>', self.one)
# 为左键双击事件绑定处理方法
bn.bind('<Double-1>', self.double)

def one(self):
self.show['text'] = "你执行了单击"

def double(self, event):
print("左键双击击, 退出程序:", event.widget['text'])
sys.exit()


root = Tk()
root.title('简单绑定')
App(root)
root.mainloop()

运行

tk00013

Tkinter 支持的各种鼠标、键盘事件

事件简介
<Button-detail>鼠标按键的单击事件,detail 指定哪一个鼠标键被单击。比如单击鼠标左键为 <Button-1>,单击鼠标中键为 <Button-2>,单击鼠标右键为 <Button-3>,单击向上滚动的滚轮为 <Button-4>,单击向下滚动的滚轮为 <Button-5>
<\modifier \Motion>鼠标在组件上的移动事件,modifier 指定要求按住哪个鼠标键。比如按住鼠标左键移动为 <B1-Motion>,锁住鼠杯中键移动为 <B2-Motion>,按住鼠标右键移动为 <B3-Motion>
<ButtonRelease-detail>鼠标按键的释放事件,detail 指定哪一个鼠标键被释放。比如鼠标左键被释放为 <ButtonRelease-1>,鼠标中键被释放为 <ButtonRelease-2>,鼠标右键被释放为 <ButtonRelease-3>
<Double-Button-detail>或<Double-detail>用户双击某个鼠标键的事件,detail 指定哪一个鼠标键被双击。比如双击鼠标左键为 <Double-1>,双击鼠标中键为 <Double-2>,双击鼠标右键为 <Double-3>,双击向上滚动的滚轮为 <Double-4>,双击向下滚动的滚轮为 <Double-5>
<Enter>鼠标进入组件的事件。注意,<Enter> 事件不是按下回车键事件,按下回车键的事件是 <Return>
<Leave>鼠标移出组件事件
<Focusln>组件及其包含的子组件获得焦点
<FocusOut>组件及其包含的子组件失去焦点
<Return>按下回车键的事件。实际上可以为所有按键绑定事件处理方法。特殊键位名称包括 Cancel、BackSpace、Tab、Return(回车)、Shift_L(左Shift,如果只写 Shift 则代表任意 Shift)、Control_L(左 Ctrl,如果只写 Control 则代表任意 Ctrl)、Alt_L(左 Alt,如果只写 Alt 则代表任意 Alt)、Pause、Caps_Lock、Escape、Prior(Page Up)、Next(Page Down)、End、Home、Left、Up、Right、Down、Print、Insert、Delete、F1、F2、F3、F4、F5、F6、F7、F8、F9、F10、F11、F12、Num_Lock 和 Scroll_Lock
<Key>键盘上任意键的单击事件,程序可通过 event 获取用户单击了哪个键
a键盘上指定键被单击的事件。比如‘a’代表 a 键被单击,‘b’代表 b 键被单击(不要尖括号)……
<Shift-Up>在 Shift 键被按下时按 Up 键。类似的还有 <Shift-Left>、<Shift-Down>、<Alt-Up>、<Control-Up> 等
<Configure>组件大小、位置改变的事件。组件改变之后的大小、位置可通过 event 的 width、height、x、y 获取

鼠标移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from tkinter import *


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
lb = Label(self.master, width=40, height=3)
lb.config(bg='lightgreen', font=('Times', 20))
# 为鼠标移动事件绑定事件处理方法
lb.bind('<Motion>', self.motion)
# 为按住左键时的鼠标移动事件绑定事件处理方法
lb.bind('<B1-Motion>', self.press_motion)
lb.pack()
self.show = Label(self.master, width=38, height=1)
self.show.config(bg='white', font=('Courier New', 20))
self.show.pack()

def motion(self, event):
self.show['text'] = "鼠标移动到: (%s %s)" % (event.x, event.y)
return

def press_motion(self, event):
self.show['text'] = "按住鼠标的位置为: (%s %s)" % (event.x, event.y)
return


root = Tk()
root.title('鼠标事件')
App(root)
root.mainloop()

运行

tk00016

ttk组件

tkinter 模块下的 GUI 组件比较丑陋,ttk组件主要是简单的包装和美化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from tkinter import *
# 导入ttk
from tkinter import ttk
class App:
def __init__(self, master):
self.master = master
self.initWidgets()
def initWidgets(self):
# ttk使用Combobox取代了Listbox
# cb = ttk.Combobox(self.master, font=24)
# 为Combobox设置列表项
# cb['values'] = ('Python', 'Swift', 'Kotlin')
cb = Listbox(self.master, font=24)
# 为Listbox设置列表项
for s in ('Python', 'Swift', 'Kotlin'):
cb.insert(END, s)
cb.pack(side=LEFT, fill=X, expand=YES)
# f = ttk.Frame(self.master)
f = Frame(self.master)
f.pack(side=RIGHT, fill=BOTH, expand=YES)
# lab = ttk.Label(self.master, text='我的标签', font=24)
lab = Label(self.master, text='我的标签', font=24)
lab.pack(side=TOP, fill=BOTH, expand=YES)
# bn = ttk.Button(self.master, text='我的按钮')
bn = Button(self.master, text='我的按钮')
bn.pack()
root = Tk()
root.title("简单事件处理")
App(root)
root.mainloop()

执行

使用ttk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# ttk使用Combobox取代了Listbox
# cb = ttk.Combobox(self.master, font=24)
# 为Combobox设置列表项
# cb['values'] = ('Python', 'Swift', 'Kotlin')
cb = Listbox(self.master, font=24)
# 为Listbox设置列表项
for s in ('Python', 'Swift', 'Kotlin'):
cb.insert(END, s)
cb.pack(side=LEFT, fill=X, expand=YES)
f = ttk.Frame(self.master)
f.pack(side=RIGHT, fill=BOTH, expand=YES)
lab = ttk.Label(self.master, text='我的标签', font=24)
# lab = Label(self.master, text='我的标签', font=24)
lab.pack(side=TOP, fill=BOTH, expand=YES)
bn = ttk.Button(self.master, text='我的按钮')
# bn = Button(self.master, text='我的按钮')
bn.pack()


root = Tk()
root.title("简单事件处理")
App(root)
root.mainloop()

执行

对上面的两个界面做对比,第二个会更好看一些

variable组件

为了让 Tkinter 组件与变量进行双向绑定,只要为这些组件指定 variable(通常绑定组件的 value)、textvariable(通常绑定组件显示的文本)等属性即可。

但这种双向绑定有一个限制,就是 Tkinter不允许将组件和普通变量进行绑定,只能和 tkinter 包下 Variable 类的子类进行绑定。该类包含如下几个子类:

  • ShringVar():用于包装str 值的变量。
  • IntVar():用于包装整型值的变量。
  • DoubleVar():用于包装浮点值的变量。
  • BooleanVar():用于包装bool值的变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from tkinter import *
# 导入ttk
from tkinter import ttk
import random
from tkinter import messagebox


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.st = StringVar()
# 创建Entry组件,将其textvariable绑定到self.st变量
ttk.Entry(self.master, textvariable=self.st,
width=24,
font=('StSong', 20, 'bold'),
foreground='red').pack(fill=BOTH, expand=YES)
# 创建Frame作为容器
f = Frame(self.master)
f.pack()
# 创建两个按钮,将其放入Frame中
ttk.Button(f, text='改变', command=self.change).pack(side=LEFT)
ttk.Button(f, text='获取', command=self.get).pack(side=LEFT)

def change(self):
books = ('Python-A', 'Python-B', 'Python-C')
# 改变self.st变量的值,与之绑定的Entry的内容随之改变
self.st.set(books[random.randint(0, 2)])

def get(self):
# 获取self.st变量的值,实际上就是获取与之绑定的Entry中的内容
# 并使用消息框显示self.st变量的值
messagebox.showinfo(title='输入内容', message=self.st.get())


root = Tk()
root.title("variable测试")
App(root)
root.mainloop()

运行

tk00020

tk00021

compound

程序可以为按钮或 Label 等组件同时指定 text 和 image 两个选项,其中 text 用于指定该组件上的文本;image 用于显示该组件上的图片,当同时指定这两个选项时,通常 image 会覆盖 text。

compound 选项支持如下属性值:

  • None:图片覆盖文字。
  • LEFT 常量(值为‘left’字符串):图片在左,文本在右。
  • RIGHT 常量(值为‘right’字符串):图片在右,文本在左。
  • TOP 常量(值为‘top’字符串): 图片在上,文本在下。
  • BOTTOM 常量(值为‘bottom’字符串):图片在底,文本在上。
  • CENTER 常量(值为‘center’字符串):文本在图片上方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建一个位图
bm = PhotoImage(file='1.png')
# 创建一个Label,同时指定text和image
self.label = ttk.Label(self.master, text='Python\n Im',
image=bm, font=('StSong', 20, 'bold'), foreground='red')
self.label.bm = bm
# 设置Label默认的compound为None
self.label['compound'] = None
self.label.pack()
# 创建Frame容器,用于装多个Radiobutton
f = ttk.Frame(self.master)
f.pack(fill=BOTH, expand=YES)
compounds = ('None', "LEFT", "RIGHT", "TOP", "BOTTOM", "CENTER")
# 定义一个StringVar变量,用作绑定Radiobutton的变量
self.var = StringVar()
self.var.set('None')
# 使用循环创建多个Radionbutton组件
for val in compounds:
rb = Radiobutton(f,
text=val,
padx=20,
variable=self.var,
command=self.change_compound,
value=val).pack(side=LEFT, anchor=CENTER)

# 实现change_compound方法,用于动态改变Label的compound选项
def change_compound(self):
self.label['compound'] = self.var.get().lower()


root = Tk()
root.title("compound测试")
App(root)
root.mainloop()

运行

tk00023

tk00024

Entry和Text控件

Entry 和 Text 组件都是可接收用户输入的输入框组件,区别是 Entry 是单行输入框组件,Text 是多行输入框组件,而且 Text 可以为不同的部分添加不同的格式,甚至响应事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from tkinter import *
# 导入ttk
from tkinter import ttk
from tkinter import messagebox


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建Entry组件
self.entry = ttk.Entry(self.master,
width=44,
font=('StSong', 14),
foreground='green')
self.entry.pack(fill=BOTH, expand=YES)
# 创建Entry组件
self.text = Text(self.master,
width=44,
height=4,
font=('StSong', 14),
foreground='gray')
self.text.pack(fill=BOTH, expand=YES)
# 创建Frame作为容器
f = Frame(self.master)
f.pack()
# 创建五个按钮,将其放入Frame中
ttk.Button(f, text='开始插入', command=self.insert_start).pack(side=LEFT)
ttk.Button(f, text='编辑处插入', command=self.insert_edit).pack(side=LEFT)
ttk.Button(f, text='结尾插入', command=self.insert_end).pack(side=LEFT)
ttk.Button(f, text='获取Entry', command=self.get_entry).pack(side=LEFT)
ttk.Button(f, text='获取Text', command=self.get_text).pack(side=LEFT)

def insert_start(self):
# 在Entry和Text开始处插入内容
self.entry.insert(0, 'Kotlin')
self.text.insert(1.0, 'Kotlin')

def insert_edit(self):
# 在Entry和Text的编辑处插入内容
self.entry.insert(INSERT, 'Python')
self.text.insert(INSERT, 'Python')

def insert_end(self):
# 在Entry和Text的结尾处插入内容
self.entry.insert(END, 'Swift')
self.text.insert(END, 'Swift')

def get_entry(self):
messagebox.showinfo(title='输入内容', message=self.entry.get())

def get_text(self):
messagebox.showinfo(title='输入内容', message=self.text.get(1.0, END))


root = Tk()
root.title("Entry测试")
App(root)
root.mainloop()

执行

Radiobutton和Checkbutton控件用法

Radiobutton 组件代表单选钮,该组件可以绑定一个方法或函数,当单选钮被选择时,该方法或函数将会被触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建一个Label组件
ttk.Label(self.master, text='选择您喜欢的教程:') \
.pack(fill=BOTH, expand=YES)
self.intVar = IntVar()
# 定义元组
books = ('C语言入门', 'Python入门',
'C++入门', 'Java入门')
i = 1
# 采用循环创建多个Radiobutton
for book in books:
ttk.Radiobutton(self.master,
text=book,
variable=self.intVar, # 将Radiobutton绑定到self.intVar变量
command=self.change, # 将选中事件绑定到self.change方法
value=i).pack(anchor=W)
i += 1
# 设置Radiobutton绑定的变量的值为2,
# 则选中value为2的Radiobutton
self.intVar.set(2)

def change(self):
from tkinter import messagebox
# 通过Radiobutton绑定变量获取选中的单选框
messagebox.showinfo(title=None, message=self.intVar.get())


root = Tk()
root.title("Radiobutton测试")
App(root)
root.mainloop()

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建一个Label组件
ttk.Label(self.master, text='选择您喜欢的:') \
.pack(fill=BOTH, expand=YES)
self.intVar = IntVar()
# 定义元组
races = ('1.png', '2.png', '4.png')
raceNames = ('A', 'B', 'C')
i = 1
# 采用循环创建多个Radiobutton
for rc in races:
bm = PhotoImage(file=rc)
r = ttk.Radiobutton(self.master,
image=bm,
text=raceNames[i - 1],
compound=RIGHT, # 图片在文字右边
variable=self.intVar, # 将Radiobutton绑定到self.intVar变量
command=self.change, # 将选中事件绑定到self.change方法
value=i)
r.bm = bm
r.pack(anchor=W)
i += 1
# 设置默认选中value为2的单选按钮
self.intVar.set(2)

def change(self): pass


root = Tk()
root.title("Radiobutton测试")
# 改变窗口图标
# root.iconbitmap('1.ico')
App(root)
root.mainloop()

运行

Listbox和Combobox

Listbox 代表一个列表框,用户可通过列表框来选择一个列表项。ttk 模块下的 Combobox 则是 Listbox 的改进版,它既提供了单行文本框让用户直接输入(就像 Entry 一样),也提供了下拉列表框供用户选择(就像 Listbox 一样),因此它被称为复合框。

程序创建 Listbox 起码需要两步:

  • 创建 Listbox 对象,并为之执行各种选项。Listbox 除支持大部分通用选项之外,还支持 selectmode 选项,用于设置 Listbox 的选择模式。
  • 调用 Listbox 的 insert(self, index, *elements) 方法来添加选项。从最后一个参数可以看出,该方法既可每次添加一个选项,也可传入多个参数,每次添加多个选项。index 参数指定选项的插入位置,它支持 END(结尾处)、ANCHOR(当前位置)和 ACTIVE(选中处)等特殊索引。

Listbox 的 selectmode 支持的选择模式有如下几种:

  • ‘browse’:单选模式,支持按住鼠标键拖动来改变选择。
  • ‘multiple’:多边模式。
  • ‘single’:单边模式,必须通过鼠标键单击来改变选择。
  • ‘extended’:扩展的多边模式,必须通过 CtrL 或 Shift 键辅助实现多选。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from tkinter import *
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
topF = Frame(self.master)
topF.pack(fill=Y, expand=YES)
# 创建Listbox组件
self.lb = Listbox(topF)
self.lb.pack(side=LEFT, fill=Y, expand=YES)
for item in ['Python', 'Kotlin', 'Swift', 'Ruby']:
self.lb.insert(END, item)
# 或直接使用多个元素来插入
self.lb.insert(ANCHOR, 'A', 'B', 'C', 'D', 'E', 'F', 'G')
# 创建Scrollbar组件,设置该组件与self.lb的纵向滚动关联
scroll = Scrollbar(topF, command=self.lb.yview)
scroll.pack(side=RIGHT, fill=Y)
# 设置self.lb的纵向滚动影响scroll滚动条
self.lb.configure(yscrollcommand=scroll.set)
f = Frame(self.master)
f.pack()
Label(f, text='选择模式:').pack(side=LEFT)
modes = ('multiple', 'browse', 'single', 'extended')
self.strVar = StringVar()
for m in modes:
rb = ttk.Radiobutton(f, text=m, value=m,
variable=self.strVar, command=self.choose_mode)
rb.pack(side=LEFT)
self.strVar.set('browse')

def choose_mode(self):
print(self.strVar.get())
self.lb['selectmode'] = self.strVar.get()


root = Tk()
root.title("Listbox测试")

App(root)
root.mainloop()

运行

除了最常见的 insert() 方法,Listbox 还支持如下常见的操作列表项的方法:

  • selection_set(self, first, last=None):选中从 first 到 last(包含)的所有列表项。如果不指定 last,则直接选中 first 列表项。
  • selection_clear(self, first, last=None):取消选中从 first 到 last(包含)的所有列表项。如果不指定 last,则只取消选中 first 列表项。
  • delete(self, first, last=None):删除从 first 到 last(包含)的所有列表项。如果不指定 last,则只删除 first 列表项。

Listbox 也支持使用 Iistvariable 选项与变量进行绑定,但这个变量并不是控制 Listbox 选中哪些项,而是控制 Listbox 包含哪些项。简单来说,如果 listvariable 选项与变量进行了双向绑定,则无须调用 insert()、delete() 方法来添加、删除列表项,只要通过绑定变量即可改变 Listbox 中的列表项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
topF = Frame(self.master)
topF.pack(fill=Y, expand=YES)
# 定义StringVar变量
self.v = StringVar()
# 创建Listbox组件,与v变量绑定
self.lb = Listbox(topF, listvariable=self.v)
self.lb.pack(side=LEFT, fill=Y, expand=YES)
for item in range(20):
self.lb.insert(END, str(item))
# 创建Scrollbar组件,设置该组件与self.lb的纵向滚动关联
scroll = Scrollbar(topF, command=self.lb.yview)
scroll.pack(side=RIGHT, fill=Y)
# 设置self.lb的纵向滚动影响scroll滚动条
self.lb.configure(yscrollcommand=scroll.set)
f = Frame(self.master)
f.pack()
Button(f, text="选中10项", command=self.select).pack(side=LEFT)
Button(f, text="清除选中3项", command=self.clear_select).pack(side=LEFT)
Button(f, text="删除3项", command=self.delete).pack(side=LEFT)
Button(f, text="绑定变量", command=self.var_select).pack(side=LEFT)

def select(self):
# 选中指定项
self.lb.selection_set(0, 9)

def clear_select(self):
# 取消选中指定项
self.lb.selection_clear(1, 3)

def delete(self):
# 删除指定项
self.lb.delete(5, 8)

def var_select(self):
# 修改与Listbox绑定的变量
self.v.set(('12', '15'))


root = Tk()
root.title("Listbox测试")
#
App(root)
root.mainloop()

运行

如果程序要获取 Listbox 当前边中的项,则可通过 curselection() 方法来实现,该方法会返回一个元组,该元组包含当前 Listbox 的所有选中项。

Listbox 并不支持使用 command 选项来绑定事件处理函数或方法,如果程序需要为 Listbox 绑定事件处理函数或方法,则可通过 bind() 方法来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
topF = Frame(self.master)
topF.pack(fill=Y, expand=YES)
# 创建Listbox组件
self.lb = Listbox(topF)
self.lb.pack(side=LEFT, fill=Y, expand=YES)
for item in range(20):
self.lb.insert(END, str(item))
# 创建Scrollbar组件,设置该组件与self.lb的纵向滚动关联
scroll = Scrollbar(topF, command=self.lb.yview)
scroll.pack(side=RIGHT, fill=Y)
# 设置self.lb的纵向滚动影响scroll滚动条
self.lb.configure(yscrollcommand=scroll.set)
# 为双击事件绑定事件处理方法
self.lb.bind("<Double-1>", self.click)

def click(self, event):
from tkinter import messagebox
# 获取Listbox当前选中项
messagebox.showinfo(title=None, message=str(self.lb.curselection()))


root = Tk()
root.title("Listbox测试")
#
App(root)
root.mainloop()

运行

Combobox 的用法更加简单,程序可通过 values 选项直接为它设置多个选项。该组件的 state 选项支持‘readonly’状态,该状态代表 Combobox 的文本框不允许编辑,只能通过下拉列表框的列表项来改变。

Combobox 同样可通过 textvariable 选项将它与指定变量绑定,这样程序可通过该变量来获取或修改 Combobox 组件的值。

Combobox 还可通过 postcommand 选项指定事件处理函数或方法,当用户单击 Combobox 的下拉箭头时,程序就会触发 postcomrnand 选项指定的事件处理函数或方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from tkinter import *
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.strVar = StringVar()
# 创建Combobox组件
self.cb = ttk.Combobox(self.master,
textvariable=self.strVar, # 绑定到self.strVar变量
postcommand=self.choose) # 当用户单击下拉箭头时触发self.choose方法
self.cb.pack(side=TOP)
# 为Combobox配置多个选项
self.cb['values'] = ['Python', 'Ruby', 'Kotlin', 'Swift']
f = Frame(self.master)
f.pack()
self.isreadonly = IntVar()
# 创建Checkbutton,绑定到self.isreadonly变量
Checkbutton(f, text='是否只读:',
variable=self.isreadonly,
command=self.change).pack(side=LEFT)
# 创建Button,单击该按钮激发setvalue方法
Button(f, text='绑定变量设置',
command=self.setvalue).pack(side=LEFT)

def choose(self):
from tkinter import messagebox
# 获取Combbox的当前值
messagebox.showinfo(title=None, message=str(self.cb.get()))

def change(self):
self.cb['state'] = 'readonly' if self.isreadonly.get() else 'enable'

def setvalue(self):
self.strVar.set('我爱Python')


root = Tk()
root.title("Combobox测试")
#
App(root)
root.mainloop()

运行

Spinbox 控件

Spinbox 控件是一个带有两个小箭头的文本框,用户既可以通过两个小箭头上下调整该组件内的值,也可以直接在文本框内输入内容作为该组件的值。

Spinbox 本质上也相当于持有一个列表框,这一点类似于 Combobox,但 Spinbox 不会展开下拉列表供用户选择。Spinbox 只能通过向上、向下箭头来选择不同的选项。

在使用 Spinbox 组件时,既可通过 from(由于 from 是关键字,实际使用时写成 from_)、to、increment 选项来指定选项列表,也可通过 values 选项来指定多个列表项,该选项的值可以是 list 或 tuple。

Spinbox 同样可通过 textvariable 选项将它与指定变量绑定,这样程序即可通过该变量来获取或修改 Spinbox 组件的值。

Spinbox 还可通过 command 选项指定事件处理函数或方法,即当用户单击 Spinbox 的向上、向下箭头时,程序就会触发 command 选项指定的事件处理函数或方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from tkinter import *
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
ttk.Label(self.master, text='指定from、to、increment').pack()
# 通过指定from_、to、increament选项创建Spinbox
sb1 = Spinbox(self.master, from_=20,
to=100,
increment=5)
sb1.pack(fill=X, expand=YES)
ttk.Label(self.master, text='指定values').pack()
# 通过指定values选项创建Spinbox
self.sb2 = Spinbox(self.master,
values=('Python', 'Swift', 'Kotlin', 'Ruby'),
command=self.press) # 通过command绑定事件处理方法
self.sb2.pack(fill=X, expand=YES)
ttk.Label(self.master, text='绑定变量').pack()
self.intVar = IntVar()
# 通过指定values选项创建Spinbox,并为之绑定变量
sb3 = Spinbox(self.master,
values=list(range(20, 100, 4)),
textvariable=self.intVar, # 绑定变量
command=self.press)
sb3.pack(fill=X, expand=YES)
self.intVar.set(33) # 通过变量改变Spinbox的值

def press(self):
print(self.sb2.get())


root = Tk()
root.title("Spinbox测试")
#
App(root)
root.mainloop()

运行

Scale和LabeledScale

Scale 组件代表一个滑动条,可以为该滑动条设置最小值和最大值,也可以设置滑动条每次调节的步长。

Scale 组件支持如下选项:

  • from:设置该 Scale 的最小值。
  • to:设置该 Scale 的最大值。
  • resolution:设置该 Scale 滑动时的步长。
  • label:为 Scale 组件设置标签内容。
  • length:设置轨道的长度。
  • width:设置轨道的宽度。
  • troughcolor:设置轨道的背景色。
  • sliderlength:设置轨道的长度。
  • sliderrelief:设置滑块的立体样式。
  • showvalue:设置是否显示当前值。
  • orient:设置方向。该选项支持 VERTICAL 和 HORIZONTAL 两个值。
  • digits:设置有效数字至少要有几位。
  • variable:用于与变量进行绑定。
  • command:用于为该 Scale 组件绑定事件处理,函数或方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.scale = Scale(self.master,
from_=-100, # 设置最大值
to=100, # 设置最小值
resolution=5, # 设置步长
label='示范Sacle', # 设置标签内容
length=400, # 设置轨道的长度
width=30, # 设置轨道的宽度
troughcolor='lightblue', # 设置轨道的背景色
sliderlength=20, # 设置滑块的长度
sliderrelief=SUNKEN, # 设置滑块的立体样式
showvalue=YES, # 设置显示当前值
orient=HORIZONTAL # 设置水平方向
)
self.scale.pack()
# 创建一个Frame作为容器
f = Frame(self.master)
f.pack(fill=X, expand=YES, padx=10)
Label(f, text='是否显示值:').pack(side=LEFT)
i = 0
self.showVar = IntVar()
self.showVar.set(1)
# 创建两个Radiobutton控制Scale是否显示值
for s in ('不显示', '显示'):
Radiobutton(f, text=s, value=i, variable=self.showVar,
command=self.switch_show).pack(side=LEFT)
i += 1
# 创建一个Frame作为容器
f = Frame(self.master)
f.pack(fill=X, expand=YES, padx=10)
Label(f, text='方向:').pack(side=LEFT)
i = 0
self.orientVar = IntVar()
self.orientVar.set(0)
# 创建两个Radiobutton控制Scale的方向
for s in ('水平', '垂直'):
Radiobutton(f, text=s, value=i, variable=self.orientVar,
command=self.switch_orient).pack(side=LEFT)
i += 1

def switch_show(self):
self.scale['showvalue'] = self.showVar.get()

def switch_orient(self):
# 根据单选框的选择设置orient选项的值
self.scale['orient'] = VERTICAL if self.orientVar.get() else HORIZONTAL


root = Tk()
root.title("Scale测试")
App(root)
root.mainloop()

运行

LabelFrame

Labelframe 是 Frame 容器的改进版,它允许为容器添加一个标签,该标签既可以是普通的文字标签,也可以将任意 GUI 组件作为标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建Labelframe容器
lf = ttk.Labelframe(self.master, text='请选择教程',
padding=20)
lf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
books = ['C++', 'Python', 'Linux', 'Java']
i = 0
self.intVar = IntVar()
# 使用循环创建多个Radiobutton,并放入Labelframe中
for book in books:
Radiobutton(lf, text=book + '教程',
value=i,
variable=self.intVar).pack(side=LEFT)
i += 1


root = Tk()
root.title("Labelframe测试")

App(root)
root.mainloop()

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建Labelframe容器
self.lf = ttk.Labelframe(self.master, padding=20)
self.lf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
# 创建一个显示图片的Label
bm = PhotoImage(file='1.png')
lb = Label(self.lf, image=bm)
lb.bm = bm
# 将Labelframe的标题设为显示图片的Label
self.lf['labelwidget'] = lb
# 定义代表Labelframe的标题位置的12个常量
self.books = ['e', 's', 'w', 'n', 'es', 'ws', 'en', 'wn',
'ne', 'nw', 'se', 'sw']
i = 0
self.intVar = IntVar()
# 使用循环创建多个Radiobutton,并放入Labelframe中
for book in self.books:
Radiobutton(self.lf, text=book,
value=i,
command=self.change,
variable=self.intVar).pack(side=LEFT)
i += 1
self.intVar.set(9)

def change(self):
# 通过labelanchor选项改变Labelframe的标题的位置
self.lf['labelanchor'] = self.books[self.intVar.get()]


root = Tk()
root.title("Labelframe测试")
#
App(root)
root.mainloop()

运行

Panedwindow

Panedwindow 是一个管理窗口布局的容器,它允许添加多个子组件(不需要使用 Pack、Grid 或 Place 布局)并为每个子组件划分一个区域,用户可用鼠标移动各区域的分隔线来改变各子组件的大小(如果没有显式指定大小,子细件总是自动占满整个区域)

Panedwindow 是一个非常有特色的容器,它自带布局管理功能,它允许通过 orient 选项指定水平或垂直方向,让容器中的各组件按水平或垂直方向排列。

在创建 Panedwindow 之后,程序可通过如下方法操作 Panedwindow 容器中的子组件:

  • add(self, child, **kw):添加一个子组件。
  • insert(self, pos, child,**kw):在pos 位置插入一个子组件。
  • remove(self, child):删除一个子组件,该子组件所在区域也被删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建Style
style = ttk.Style()
style.configure("fkit.TPanedwindow", background='darkgray', relief=RAISED)
# 创建Panedwindow组件,通过style属性配置分隔线
pwindow = ttk.Panedwindow(self.master,
orient=VERTICAL, style="fkit.TPanedwindow")
pwindow.pack(fill=BOTH, expand=1)
first = ttk.Label(pwindow, text="第一个标签")
# 调用add方法添加组件,每个组件一个区域
pwindow.add(first)
okBn = ttk.Button(pwindow, text="第二个按钮",
# 调用remove()方法删除组件,该组件所在区域消失
command=lambda: pwindow.remove(okBn))
# 调用add方法添加组件,每个组件一个区域
pwindow.add(okBn)
entry = ttk.Entry(pwindow, width=30)
# 调用add方法添加组件,每个组件一个区域
pwindow.add(entry)
# 调用insert方法插入组件
pwindow.insert(1, Label(pwindow, text='插入的标签'))


root = Tk()
root.title("Panedwindow测试")
App(root)
root.mainloop()

运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建Style
style = ttk.Style()
style.configure("fkit.TPanedwindow",
background='darkgray', relief=RAISED)
# 创建Panedwindow组件,通过style属性配置分隔线
pwindow = ttk.Panedwindow(self.master,
orient=HORIZONTAL, style="fkit.TPanedwindow")
pwindow.pack(fill=BOTH, expand=YES)
left = ttk.Label(pwindow, text="左边标签", background='pink')
pwindow.add(left)
# 创建第二个Panedwindow组件,该组件的方向为垂直方向
rightwindow = PanedWindow(pwindow, orient=VERTICAL)
pwindow.add(rightwindow)
top = Label(rightwindow, text="右上标签", background='lightgreen')
rightwindow.add(top)
bottom = Label(rightwindow, text="右下标签", background='lightblue')
rightwindow.add(bottom)


root = Tk()
root.title("Panedwindow测试")
App(root)
root.mainloop()

运行

OptionMenu

OptionMenu 组件用于构建一个带菜单的按钮,该菜单可以在按钮的四个方向上展开,展开方向可通过 direction 选项控制

使用 OptionMenu 比较简单,直接调用它的如下构造函数即可:

__init__ (self, master, variable, value, *values, **kwargs)

其中,master 参数的作用与所有的 Tkinker 组件一样,指定将该组件放入哪个容器中。其他参数的含义如下:

  • variable:指定该按钮上的菜单与哪个变量绑定。
  • value:指定默认选择菜单中的哪一项。
  • values:Tkinter 将收集为此参数传入的多个值,为每个值创建一个菜单项。
  • kwargs:用于为 OptionMenu 配置选项。除前面介绍的常规选项之外,还可通过 direction 选项控制菜单的展开方向。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from tkinter import *
# 导入ttk
from tkinter import ttk


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.sv = StringVar()
# 创建一个OptionMenu控件
self.om = ttk.OptionMenu(root,
self.sv, # 绑定变量
'Python', # 设置初始选中值
'Kotlin', # 以下多个值用于设置菜单项
'Ruby',
'Swift',
'Java',
'Python',
'JavaScript',
'Erlang',
command=self.print_option) # 绑定事件处理方法
self.om.pack()
# 创建Labelframe容器
lf = ttk.Labelframe(self.master, padding=20, text='请选择菜单方向')
lf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
# 定义代表Labelframe的标题位置的12个常量
self.directions = ['below', 'above', 'left', 'right', 'flush']
i = 0
self.intVar = IntVar()
# 使用循环创建多个Radiobutton,并放入Labelframe中
for direct in self.directions:
Radiobutton(lf, text=direct,
value=i,
command=self.change,
variable=self.intVar).pack(side=LEFT)
i += 1
self.intVar.set(9)

def print_option(self, val):
# 通过两种方式来获取OptionMenu选中的菜单项的值
print(self.sv.get(), val)

def change(self):
# 通过direction选项改变OptionMenu上菜单的展开方向
self.om['direction'] = self.directions[self.intVar.get()]


root = Tk()
root.title("OptionMenu测试")

App(root)
root.mainloop()

运行

Tkinter对话框

对话框也是图形界面编程中很常用的组件,通常用于向用户生成某种提示信息,或者请求用户输入某些简单的信息。

对话框看上去有点类似于顶级窗口,但对于对话框有如下两点需要注意:

  • 对话框通常依赖其他窗口,因此程序在创建对话框时同样需要指定 master 属性(该对话框的属主窗口)。
  • 对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被打开之后,该模式对话框总是位于它依赖的窗口之上;在模式对话框被关闭之前,它依赖的窗口无法获得焦点。

Tkinter 在 simpledialog 和 dialog 模块下分别提供了 SimpleDialog 类和 Dialog 类,它们都可作为普通对话框使用,而且用法也差不多。

在使用 simpledialog.SimpleDialog 创建对话框时,可指定如下选项:

  • title:指定该对话框的标题。
  • text:指定该对话框的内容。
  • button:指定该对话框下方的几个按钮。
  • default:指定该对话框中默认第几个按钮得到焦点。
  • cancel:指定当用户通过对话框右上角的X 按钮关闭对话框时,该对话框的返回值。

如果使用 dialog.Dialog 创建对话框,除可使用 master 指定对话框的属主窗口之外,还可通过 dict 来指定如下选项:

  • title:指定该对话框的标题。
  • text:指定该对话框的内容。
  • strings:指定该对话框下方的几个按钮。
  • default:指定该对话框中默认第几个按钮得到焦点。
  • bitmap:指定该对话框上的图标。

对比上面介绍不难发现,simpledialog.SimpleDialog 和 dialog.Dialog 所支持的选项大同小异,区别只是 dialog.Dialog 需要使用 dict 来传入多个选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from tkinter import *
# 导入ttk
from tkinter import ttk
# 导入simpledialog
from tkinter import simpledialog
# 导入dialog
from tkinter import dialog


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.msg = 'Hello World'
# 创建2个按钮,并为之绑定事件处理函数
ttk.Button(self.master, text='打开SimpleDialog',
command=self.open_simpledialog # 绑定open_simpledialog方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='打开Dialog',
command=self.open_dialog # 绑定open_dialog方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)

def open_simpledialog(self):
# 使用simpledialog.SimpleDialog创建对话框
d = simpledialog.SimpleDialog(self.master, # 设置该对话框所属的窗口
title='SimpleDialog测试', # 标题
text=self.msg, # 内容
buttons=["是", "否", "取消"],
cancel=3,
default=0 # 设置默认是哪个按钮得到焦点
)
print(d.go())

def open_dialog(self):
# 使用dialog.Dialog创建对话框
d = dialog.Dialog(self.master # 设置该对话框所属的窗口
, {'title': 'Dialog测试', # 标题
'text': self.msg, # 内容
'bitmap': 'question', # 图标
'default': 0, # 设置默认选中项
# strings选项用于设置按钮
'strings': ('确定',
'取消',
'退出')})
print(d.num)


root = Tk()
root.title("对话框")
App(root)
root.mainloop()

运行

tk00040

自定义对话框

从前面章节的介绍可以看到,不管是使用 SimpleDialog 还是 Dialog,整个对话框的布局都是比较固定的,开发者只能为其指定 title、text 等选项,如果希望在对话框中添加其他组件,这就很难实现了。另外,SimpleDialog 和 Dialog 都是模式的。

如果开发者需要使用自定义的对话框,包括定制模式和非模式行为,则可通过继承 Toplevel 来实现。如果打算通过这种方式来实现自定义对话框,有两点要注意:

继承 Toplevel 来实现自定义对话框同样需要为对话框指定 master。
程序可调用 Toplevel 的 grab_set() 方法让该对话框变成模式对话框,否则就是非模式对话框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from tkinter import *
# 导入ttk
from tkinter import ttk
from tkinter import messagebox


# 自定义对话框类,继承Toplevel
class MyDialog(Toplevel):
# 定义构造方法
def __init__(self, parent, title=None, modal=True):
Toplevel.__init__(self, parent)
self.transient(parent)
# 设置标题
if title: self.title(title)
self.parent = parent
self.result = None
# 创建对话框的主体内容
frame = Frame(self)
# 调用init_widgets方法来初始化对话框界面
self.initial_focus = self.init_widgets(frame)
frame.pack(padx=5, pady=5)
# 调用init_buttons方法初始化对话框下方的按钮
self.init_buttons()
# 根据modal选项设置是否为模式对话框
if modal: self.grab_set()
if not self.initial_focus:
self.initial_focus = self
# 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法
self.protocol("WM_DELETE_WINDOW", self.cancel_click)
# 根据父窗口来设置对话框的位置
self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50))
print(self.initial_focus)
# 让对话框获取焦点
self.initial_focus.focus_set()
self.wait_window(self)

# 通过该方法来创建自定义对话框的内容
def init_widgets(self, master):
# 创建并添加Label
Label(master, text='用户名', font=12, width=10).grid(row=1, column=0)
# 创建并添加Entry,用于接受用户输入的用户名
self.name_entry = Entry(master, font=16)
self.name_entry.grid(row=1, column=1)
# 创建并添加Label
Label(master, text='密 码', font=12, width=10).grid(row=2, column=0)
# 创建并添加Entry,用于接受用户输入的密码
self.pass_entry = Entry(master, font=16)
self.pass_entry.grid(row=2, column=1)

# 通过该方法来创建对话框下方的按钮框
def init_buttons(self):
f = Frame(self)
# 创建"确定"按钮,位置绑定self.ok_click处理方法
w = Button(f, text="确定", width=10, command=self.ok_click, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
# 创建"确定"按钮,位置绑定self.cancel_click处理方法
w = Button(f, text="取消", width=10, command=self.cancel_click)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok_click)
self.bind("<Escape>", self.cancel_click)
f.pack()

# 该方法可对用户输入的数据进行校验
def validate(self):
# 可重写该方法
return True

# 该方法可处理用户输入的数据
def process_input(self):
user_name = self.name_entry.get()
user_pass = self.pass_entry.get()
messagebox.showinfo(message='用户输入的用户名: %s, 密码: %s'
% (user_name, user_pass))

def ok_click(self, event=None):
print('确定')
# 如果不能通过校验,让用户重新输入
if not self.validate():
self.initial_focus.focus_set()
return
self.withdraw()
self.update_idletasks()
# 获取用户输入数据
self.process_input()
# 将焦点返回给父窗口
self.parent.focus_set()
# 销毁自己
self.destroy()

def cancel_click(self, event=None):
print('取消')
# 将焦点返回给父窗口
self.parent.focus_set()
# 销毁自己
self.destroy()


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建2个按钮,并为之绑定事件处理函数
ttk.Button(self.master, text='模式对话框',
command=self.open_modal # 绑定open_modal方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='非模式对话框',
command=self.open_none_modal # 绑定open_none_modal方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)

def open_modal(self):
d = MyDialog(self.master, title='模式对话框') # 默认是模式对话框

def open_none_modal(self):
d = MyDialog(self.master, title='非模式对话框', modal=False)


root = Tk()
root.title("颜色对话框测试")
App(root)
root.mainloop()

运行

tk00042

输入对话框

在 simpledialog 模块下还有如下便捷的工具函数,通过这些工具函数可以更方便地生成各种输入对话框:

  • askinteger:生成一个让用户输入整数的对话框。
  • askfloat:生成一个让用户输入浮点数的对话框。
  • askstring:生成一个让用户输入字符串的对话框。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from tkinter import *
from tkinter import ttk
from tkinter import simpledialog


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建3个按钮,并为之绑定事件处理函数
ttk.Button(self.master, text='输入整数对话框',
command=self.open_integer # 绑定open_integer方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='输入浮点数对话框',
command=self.open_float # 绑定open_integer方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='输入字符串对话框',
command=self.open_string # 绑定open_integer方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)

def open_integer(self):
# 调用askinteger函数生成一个让用户输入整数的对话框
print(simpledialog.askinteger("猜糖果", "你猜我手上有几个糖果:",
initialvalue=3, minvalue=1, maxvalue=10))

def open_float(self):
# 调用askfloat函数生成一个让用户输入浮点数的对话框
print(simpledialog.askfloat("猜体重", "你猜我我体重多少公斤:",
initialvalue=27.3, minvalue=10, maxvalue=50))

def open_string(self):
# 调用askstring函数生成一个让用户输入字符串的对话框
print(simpledialog.askstring("猜名字", "你猜我叫什么名字:",
initialvalue='Charlie'))


root = Tk()
root.title("输入对话框测试")
App(root)
root.mainloop()

运行

文件对话框

在 filedialog 模块下提供了各种用于生成文件对话框的工具函数,如下所示。这些工具函数有些返回用户所选择文件的路径,有些直接返回用户所选择文件的输入/输出流:

askopenfile():生成打开单个文件的对话框,返回所选择文件的文件流,程序可通过该文件流来读取文件内容。
askopenfiles():生成打开多个文件的对话框,返回多个所选择文件的文件流组成的列表,程序可通过这些文件流来读取文件内容。
askopenfilename():生成打开单个文件的对话框,返回所选择文件的文件路径。
askopenfilenames():生成打开多个文件的对话框,返回多个所选择文件的文件路径组成的元组。
asksaveasfile():生成保存文件的对话框,返回所选择文件的文件输出流,程序可通过该文件输出流向文件写入数据。
asksaveasfilename():生成保存文件的对话框,返回所选择文件的文件路径。
askdirectory():生成打开目录的对话框。

上面的用于生成打开文件的对话框的工具函数支持如下选项:
defaultextension:指定默认扩展名。当用户没有输入扩展名时,系统会默认添加该选项指定的扩展名。

filetypes:指定在该文件对话框中能查看的文件类型。该选项值是一个序列,可指定多个文件类型。可以通过“*”指定浏览所有文件。
initialdir:指定初始打开的目录。
initialfile:指定所选择的文件。
parent:指定该对话框的属主窗口。
title:指定对话框的标题。
multiple:指定是否允许多选。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from tkinter import *
# 导入ttk
from tkinter import ttk
# 导入filedialog
from tkinter import filedialog


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建7个按钮,并为之绑定事件处理函数
ttk.Button(self.master, text='打开单个文件',
command=self.open_file # 绑定open_file方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='打开多个文件',
command=self.open_files # 绑定open_files方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='获取单个打开文件的文件名',
command=self.open_filename # 绑定open_filename方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='获取多个打开文件的文件名',
command=self.open_filenames # 绑定open_filenames方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='获取保存文件',
command=self.save_file # 绑定save_file方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='获取保存文件的文件名',
command=self.save_filename # 绑定save_filename方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)
ttk.Button(self.master, text='打开路径',
command=self.open_dir # 绑定open_dir方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)

def open_file(self):
# 调用askopenfile方法获取单个打开的文件
print(filedialog.askopenfile(title='打开单个文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def open_files(self):
# 调用askopenfile方法获取多个打开的文件
print(filedialog.askopenfiles(title='打开多个文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def open_filename(self):
# 调用askopenfilename方法获取单个文件的文件名
print(filedialog.askopenfilename(title='打开单个文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def open_filenames(self):
# 调用askopenfilenames方法获取多个文件的文件名
print(filedialog.askopenfilenames(title='打开多个文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def save_file(self):
# 调用asksaveasfile方法保存文件
print(filedialog.asksaveasfile(title='保存文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def save_filename(self):
# 调用asksaveasfilename方法获取保存文件的文件名
print(filedialog.asksaveasfilename(title='保存文件',
filetypes=[("文本文件", "*.txt"), ('Python源文件', '*.py')], # 只处理的文件类型
initialdir='g:/')) # 初始目录

def open_dir(self):
# 调用askdirectory方法打开目录
print(filedialog.askdirectory(title='打开目录',
initialdir='g:/')) # 初始目录


root = Tk()
root.title("文件对话框测试")
App(root)
root.mainloop()

运行

askcolor颜色选择对话框

在 colorchooser 模块下提供了用于生成颜色选择对话框的 askcolor() 工具函数,为该工具函数可指定如下选项:

parent:指定该对话框的属主窗口。
title:指定该对话框的标题。
color:指定该对话框初始选择的颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from tkinter import *
# 导入ttk
from tkinter import ttk
# 导入colorchooser
from tkinter import colorchooser


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# 创建1个按钮,并为之绑定事件处理函数
ttk.Button(self.master, text='选择颜色',
command=self.choose_color # 绑定choose_color方法
).pack(side=LEFT, ipadx=5, ipady=5, padx=10)

def choose_color(self):
# 调用askcolor函数获取选中的颜色
print(colorchooser.askcolor(parent=self.master, title='选择画笔颜色',
color='blue')) # 初始颜色


root = Tk()
root.title("颜色对话框测试")
App(root)
root.mainloop()

运行

消息框

messagebox 模块下提供了大量工具函数来生成各种消息框

在默认情况下,开发者在调用 messagebox 的工具函数时只要设置提示区的字符串即可,图标区的图标、按钮区的按钮都有默认设置。

如果有必要,则完全可通过如下两个选项来定制图标和按钮:

icon:定制图标的选I页。该选项支持“error”、“info”、“question”、“warning”这几个选项值。
type:定制按钮的选项。该选项支持“abortretryignore”(取消、重试、忽略)、“ok”(确定)、“okcancel”(确定、取消)、“retrycancel”(重试、取消)、“yesno”(是、否)、“yesnocancel”(是、否、取消)这些选项值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
from tkinter import *
# 导入ttk
from tkinter import ttk
# 导入messagebox
from tkinter import messagebox as msgbox


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
# -----------创建第1个Labelframe,用于选择图标类型-----------
topF = Frame(self.master)
topF.pack(fill=BOTH)
lf1 = ttk.Labelframe(topF, text='请选择图标类型')
lf1.pack(side=LEFT, fill=BOTH, expand=YES, padx=10, pady=5)
i = 0
self.iconVar = IntVar()
self.icons = [None, "error", "info", "question", "warning"]
# 使用循环创建多个Radiobutton,并放入Labelframe中
for icon in self.icons:
Radiobutton(lf1, text=icon if icon is not None else '默认',
value=i,
variable=self.iconVar).pack(side=TOP, anchor=W)
i += 1
self.iconVar.set(0)
# -----------创建第二个Labelframe,用于选择按钮类型-----------
lf2 = ttk.Labelframe(topF, text='请选择按钮类型')
lf2.pack(side=LEFT, fill=BOTH, expand=YES, padx=10, pady=5)
i = 0
self.typeVar = IntVar()
# 定义所有按钮类型
self.types = [None, "abortretryignore", "ok", "okcancel",
"retrycancel", "yesno", "yesnocancel"]
# 使用循环创建多个Radiobutton,并放入Labelframe中
for tp in self.types:
Radiobutton(lf2, text=tp if tp is not None else '默认',
value=i,
variable=self.typeVar).pack(side=TOP, anchor=W)
i += 1
self.typeVar.set(0)
# -----------创建Frame,用于包含多个按钮来生成不同的消息框-----------
bottomF = Frame(self.master)
bottomF.pack(fill=BOTH)
# 创建8个按钮,并为之绑定事件处理函数
btn1 = ttk.Button(bottomF, text="showinfo",
command=self.showinfo_clicked)
btn1.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn2 = ttk.Button(bottomF, text="showwarning",
command=self.showwarning_clicked)
btn2.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn3 = ttk.Button(bottomF, text="showerror",
command=self.showerror_clicked)
btn3.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn4 = ttk.Button(bottomF, text="askquestion",
command=self.askquestion_clicked)
btn4.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn5 = ttk.Button(bottomF, text="askokcancel",
command=self.askokcancel_clicked)
btn5.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn6 = ttk.Button(bottomF, text="askyesno",
command=self.askyesno_clicked)
btn6.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn7 = ttk.Button(bottomF, text="askyesnocancel",
command=self.askyesnocancel_clicked)
btn7.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)
btn8 = ttk.Button(bottomF, text="askretrycancel",
command=self.askretrycancel_clicked)
btn8.pack(side=LEFT, fill=X, ipadx=5, ipady=5,
pady=5, padx=5)

def showinfo_clicked(self):
print(msgbox.showinfo("Info", "showinfo测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def showwarning_clicked(self):
print(msgbox.showwarning("Warning", "showwarning测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def showerror_clicked(self):
print(msgbox.showerror("Error", "showerror测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def askquestion_clicked(self):
print(msgbox.askquestion("Question", "askquestion测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def askokcancel_clicked(self):
print(msgbox.askokcancel("OkCancel", "askokcancel测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def askyesno_clicked(self):
print(msgbox.askyesno("YesNo", "askyesno测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def askyesnocancel_clicked(self):
print(msgbox.askyesnocancel("YesNoCancel", "askyesnocancel测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))

def askretrycancel_clicked(self):
print(msgbox.askretrycancel("RetryCancel", "askretrycancel测试.",
icon=self.icons[self.iconVar.get()],
type=self.types[self.typeVar.get()]))


root = Tk()
root.title("消息框测试")
App(root)
root.mainloop()

运行

Menu菜单

Tkinter 为菜单提供了 Menu 类,该类既可代表菜单条,也可代表菜单,还可代表上下文菜单(右键菜单)。简单来说,Menu 类就可以搞定所有菜单相关内容。

程序可调用 Menu 的构造方法来创建菜单,在创建菜单之后可通过如下方法添加菜单项:

add_command():添加菜单项。
add_checkbutton():添加复选框菜单项。
add_radiobutton():添加单选钮菜单项。
add_separator():添加菜单分隔条。

上面的前三个方法都用于添加菜单项,因此都支持如下常用选项:

label:指定菜单项的文本。
command:指定为菜单项绑定的事件处理方法。
image:指定菜单项的图标。
compound:指定在菜单项中图标位于文字的哪个方位。

有了菜单之后,接下来就是如何使用菜单了。菜单有两种用法:

在窗口上方通过菜单条管理菜单。
通过鼠标右键触发右键菜单(上下文菜单)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from tkinter import *
# 导入ttk
from tkinter import ttk
from tkinter import messagebox as msgbox


class App:
def __init__(self, master):
self.master = master
self.init_menu()

# 创建菜单
def init_menu(self):
# 创建menubar,它被放入self.master中
menubar = Menu(self.master)
self.master.filenew_icon = PhotoImage(file='1.png')
self.master.fileopen_icon = PhotoImage(file='2.png')
# 添加菜单条
self.master['menu'] = menubar
# 创建file_menu菜单,它被放入menubar中
file_menu = Menu(menubar, tearoff=0)
# 使用add_cascade方法添加file_menu菜单
menubar.add_cascade(label='文件', menu=file_menu)
# 创建lang_menu菜单,它被放入menubar中
lang_menu = Menu(menubar, tearoff=0)
# 使用add_cascade方法添加lang_menu菜单
menubar.add_cascade(label='选择语言', menu=lang_menu)
# 使用add_command方法为file_menu添加菜单项
file_menu.add_command(label="新建", command=None,
image=self.master.filenew_icon, compound=LEFT)
file_menu.add_command(label="打开", command=None,
image=self.master.fileopen_icon, compound=LEFT)
# 使用add_command方法为file_menu添加分隔条
file_menu.add_separator()
# 为file_menu创建子菜单
sub_menu = Menu(file_menu, tearoff=0)
# 使用add_cascade方法添加sub_menu子菜单
file_menu.add_cascade(label='选择性别', menu=sub_menu)
self.genderVar = IntVar()
# 使用循环为sub_menu子菜单添加菜单项
for i, im in enumerate(['男', '女', '保密']):
# 使用add_radiobutton方法为sub_menu子菜单添加单选菜单项
# 绑定同一个变量,说明它们是一组
sub_menu.add_radiobutton(label=im, command=self.choose_gender,
variable=self.genderVar, value=i)
self.langVars = [StringVar(), StringVar(), StringVar(), StringVar()]
# 使用循环为lang_menu菜单添加菜单项
for i, im in enumerate(('Python', 'Kotlin', 'Swift', 'Java')):
# 使用add_add_checkbutton方法为lang_menu菜单添加多选菜单项
lang_menu.add_checkbutton(label=im, command=self.choose_lang,
onvalue=im, variable=self.langVars[i])

def choose_gender(self):
msgbox.showinfo(message=('选择的性别为: %s' % self.genderVar.get()))

def choose_lang(self):
rt_list = [e.get() for e in self.langVars]
msgbox.showinfo(message=('选择的语言为: %s' % ','.join(rt_list)))


root = Tk()
root.title("菜单测试")
root.geometry('400x200')
# 禁止改变窗口大小
root.resizable(width=False, height=False)
App(root)
root.mainloop()

运行

右键菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from tkinter import *
# 导入ttk
from tkinter import ttk
from collections import OrderedDict


class App:
def __init__(self, master):
self.master = master
self.initWidgets()

def initWidgets(self):
self.text = Text(self.master, height=12, width=60,
foreground='darkgray',
font=('微软雅黑', 12),
spacing2=8, # 设置行间距
spacing3=12) # 设置段间距
self.text.pack()
st = 'Hello World...'
self.text.insert(END, st)
# 为text组件的右键单击事件绑定处理方法
self.text.bind('<Button-3>', self.popup)
# 创建Menu对象,准备作为右键菜单
self.popup_menu = Menu(self.master, tearoff=0)
self.my_items = (OrderedDict([('超大', 16), ('大', 14), ('中', 12),
('小', 10), ('超小', 8)]),
OrderedDict([('红色', 'red'), ('绿色', 'green'), ('蓝色', 'blue')]))
i = 0
for k in ['字体大小', '颜色']:
m = Menu(self.popup_menu, tearoff=0)
# 添加子菜单
self.popup_menu.add_cascade(label=k, menu=m)
# 遍历OrderedDict的key(默认就是遍历key)
for im in self.my_items[i]:
m.add_command(label=im, command=self.handlerAdaptor(self.choose, x=im))
i += 1

def popup(self, event):
# 在指定位置显示菜单
self.popup_menu.post(event.x_root, event.y_root) # ①

def choose(self, x):
# 如果用户选择修改字体大小的子菜单项
if x in self.my_items[0].keys():
# 改变字体大小
self.text['font'] = ('微软雅黑', self.my_items[0][x])
# 如果用户选择修改颜色的子菜单项
if x in self.my_items[1].keys():
# 改变颜色
self.text['foreground'] = self.my_items[1][x]

def handlerAdaptor(self, fun, **kwds):
return lambda fun=fun, kwds=kwds: fun(**kwds)


root = Tk()
root.title("右键菜单测试")
App(root)
root.mainloop()

运行

Canvas画布

Tkinter 提供了 Canvas 组件来实现绘图。程序既可在 Canvas 中绘制直线、矩形、椭圆等各种几何图形,也可绘制图片、文字、UI 组件(如 Button)等。Canvas 允许重新改变这些图形项(Tkinter 将程序绘制的所有东西统称为 item)的属性,比如改变其坐标、外观等。

#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from tkinter import *

myWindow = Tk()
myWindow.title("简单绘画")
myWindow.geometry("400x300+300+200")

# width,height:设置画布的宽高,bg:设置背景色
can = Canvas(myWindow, width=400, height=300, bg="orange")
# 绘制一条线,起点--终点,线宽
can.create_line((0, 0), (200, 200), width=4)
# 绘制文字,前两个参数为字的位置
can.create_text(300, 30, text="绘制", font=("Arial", 18))
# 布局方式
can.pack()
# 进入消息循环
myWindow.mainloop()

运行

绘制矩形和直线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from tkinter import *

master = Tk()

w = Canvas(master, width=300, height=200)
w.pack()

w.create_rectangle(50, 20, 250, 180, fill="#476042")
w.create_rectangle(65, 35, 235, 165, fill="yellow")
w.create_line(0, 0, 50, 20, fill="#476042", width=3)
w.create_line(0, 200, 50, 180, fill="#476042", width=3)
w.create_line(250, 20, 300, 0, fill="#476042", width=3)
w.create_line(250, 180, 300, 200, fill="#476042", width=3)

mainloop()

运行