ZetCode-Python-教程-一-

ZetCode Python 教程(一)

原文:ZetCode

协议:CC BY-NC-SA 4.0

Python 运算符

原文: http://zetcode.com/lang/python/operators/

在 Python 编程教程的这一部分中,我们介绍了 Python 运算符。

运算符是特殊符号,表示已执行某个过程。 编程语言的运算符来自数学。 应用处理数据。 运算符用于处理数据。

在 Python 中,我们有几种类型的运算符:

  • 算术运算符
  • 布尔运算符
  • 关系运算符
  • 按位运算符

一个运算符可以有一个或两个操作数。 操作数是运算符的输入(参数)之一。 仅使用一个操作数的那些运算符称为一元运算符。 那些使用两个操作数的对象称为二进制运算符。

+和-可以是加减运算符,也可以是一元符号运算符。 这取决于实际情况。

>>> 2
2
>>> +2
2
>>> 

加号可以用来表示我们有一个正数。 但是它通常不被使用。 减号更改值的符号。

>>> a = 1
>>> -a
-1
>>> -(-a)
1

乘法和加法运算符是二进制运算符的示例。 它们与两个操作数一起使用。

>>> 3 * 3
9
>>> 3 + 3
6

Python 赋值运算符

赋值运算符=将值赋给变量。 在数学中,=运算符具有不同的含义。 在等式中,=运算符是一个相等运算符。 等式的左侧等于右侧。

>>> x = 1
>>> x
1

在这里,我们为x变量分配一个数字。

>>> x = x + 1
>>> x
2

先前的表达式在数学上没有意义。 但这在编程中是合法的。 该表达式意味着我们向x变量加 1。 右边等于 2,并且 2 分配给x

>>> a = b = c = 4
>>> print(a, b, c)
4 4 4

可以为多个变量分配一个值。

>>> 3 = y
  File "<stdin>", line 1
SyntaxError: can't assign to literal

此代码示例导致语法错误。 我们无法为字面值分配值。

Python 算术运算符

下表是 Python 编程语言中的算术运算符表。

符号 名称
+ 加成
- 减法
* 乘法
/ 除法
// 整数除法
% 模数
** 乘方

以下示例显示了算术运算。

arithmetic.py

#!/usr/bin/env python

# arithmetic.py

a = 10
b = 11
c = 12

add = a + b + c
sub = c - a
mult = a * b
div = c / 3

power = a ** 2

print(add, sub, mult, div)
print(power)

所有这些都是数学上已知的运算符。

$ ./arithmetic.py 
33 2 110 4.0
100

共有三位运算符负责部门划分。

division.py

#!/usr/bin/env python

# division.py

print(9 / 3)
print(9 / 4)
print(9 // 4)
print(9 % 4)

该示例演示了除法运算符。

print(9 / 4)

结果为 2.25。 在 Python 2.x 中,/运算符是整数除法运算符。 这在 Python 3 中已更改。在 Python 3 中,/运算符返回一个十进制数。

print(9 // 4)

//运算符是 Python 3 中的整数运算符。

print(9 % 4)

%运算符称为模运算符。 它找到一个数除以另一个的余数。 9 % 4,9 模 4 为 1,因为 4 两次进入 9 且余数为 1。

$ ./division.py 
3.0
2.25
2
1

>>> 'return' + 'of' + 'the' + 'king'
'returnoftheking'

加法运算符还可用于连接字符串。

>>> 3 + ' apples'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

我们不能添加整数和字符串。 这导致TypeError

>>> str(3) + ' apples'
'3 apples'

为了使示例生效,必须使用str()函数将数字转换为字符串。

另一方面,乘法运算符可以与字符串和数字一起使用。

>>> 'dollar ' * 5
'dollar dollar dollar dollar dollar '

Python 布尔运算符

在 Python 中,我们具有andornot布尔运算符。 使用布尔运算符,我们可以执行逻辑运算。 这些最常与ifwhile关键字一起使用。

andop.py

#!/usr/bin/env python

# andop.py

print(True and True)
print(True and False)
print(False and True)
print(False and False)

此示例显示了逻辑和运算符。 仅当两个操作数均为True时,逻辑和运算符才对True求值。

$ ./andop.py 
True
False
False
False

如果两个操作数中的任何一个为True,则逻辑或运算符求值为True

orop.py

#!/usr/bin/env python

# orop.py

print(True or True)
print(True or False)
print(False or True)
print(False or False)

如果运算符的一方为True,则操作的结果为True

$ ./orop.py 
True
True
True
False

否定运算符not使True FalseFalse True

negation.py

#!/usr/bin/env python

# negation.py

print(not False)
print(not True)
print(not ( 4 < 3 ))

该示例显示了not运算符的作用。

$ ./negation.py 
True
False
True

并且,或者对短路进行了求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时,才求值第二个参数:当和的第一个参数求值为false时,总值必须为false; 当或的第一个参数为true时,总值必须为true

以下示例演示了简短的短路求值。

short_circuit.py

#!/usr/bin/env python

# short_circuit.py

x = 10
y = 0

if (y != 0 and x/y < 100):
      print("a small value")

表达式的第一部分计算为False。 表达式的第二部分不计算。 否则,我们将得到ZeroDivisionError

Python 关系运算符

关系运算符用于比较值。 这些运算符总是产生布尔值。

符号 含义
< 小于
<= 小于或等于
> 大于
>= 大于或等于
== 等于
!= 不等于
is 对象身份
is not 否定对象身份

上表显示了 Python 关系运算符。

>>> 3 < 4
True
>>> 4 == 3
False
>>> 4 >= 3
True

如前所述,关系运算符返回布尔值:TrueFalse

注意,关系运算符不限于数字。 我们也可以将它们用于其他对象。 尽管它们可能并不总是有意义的。

>>> "six" == "six"
True
>>> 'a' < 'b'
True

我们也可以比较字符串对象。

>>> 'a' < 'b'

这里到底发生了什么? 计算机不知道字符或字符串。 对于他们来说,一切都只是数字。 字符是存储在特定表中的特殊数字,例如 ASCII。

>>> 'a' > 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() > int()

在不同的数据类型上不能使用关系运算符。 该代码导致一个TypeError

compare.py

#!/usr/bin/env python

# compare.py

print('a' < 'b')

print("a is:", ord('a'))
print("b is:", ord('b'))

在内部,a 和 b 字符是数字。 因此,当我们比较两个字符时,我们将比较它们的存储数字。 内置的ord()函数返回单个字符的 ASCII 值。

$ ./compare.py 
True
a is: 97
b is: 98

实际上,我们比较两个数字:97 和 98。

>>> "ab" > "aa"
True

假设我们有一个包含更多字符的字符串。 如果前几个字符相等,我们将比较下一个字符。 在我们的情况下,第二个位置的b字符的值比a字符大。 这就是为什么"ab"字符串大于"aa"字符串的原因。 当然,以这种方式比较字符串没有多大意义。 但这在技术上是可能的。

Python 对象身份运算符

对象标识运算符isnot is检查其操作符是否是同一对象。

object_identity.py

#!/usr/bin/env python

# object_identity.py

print(None == None)
print(None is None)

print(True is True)

print([] == [])
print([] is [])

print("Python" is "Python")

==运算符测试是否相等,而is运算符测试对象身份。 我们是否在谈论同一对象。 请注意,更多变量可能引用同一对象。

$ ./object_identity.py 
True
True
True
True
False
True

输出可能会让您感到惊讶。 在 Python 语言中,只有一个None和一个True对象。 这就是True相等且与True相同的原因。 无论如何,那里只有一个真理。 空列表[]等于另一个空列表[]。 但是它们并不相同。 Python 已将它们放入两个不同的内存位置。 它们是两个不同的对象。 因此,is运算符返回False

另一方面,"Python""Python"返回True。 这是由于优化:如果两个字符串字面值相等,则将它们放置在相同的内存位置。 由于字符串是不可变的实体,因此不会造成任何伤害。

Python 成员运算符

成员运算符innot in测试序列中的成员性,例如字符串,列表或元组。

membership.py

#!/usr/bin/env python

# membership.py

items = ("coin", "book", "pencil", "spoon", "paper")

if "coin" in items:
    print("There is a coin in the tuple")
else:
    print("There is no coin in the tuple")

if "bowl" not in items:
    print("There is no bowl in the tuple")
else:
    print("There is a bowl in the tuple")

通过成员运算符,我们可以测试元组中是否存在某个项目。

if "coin" in items:

使用in运算符,我们检查items元组中是否存在"coin"

if "bowl" not in items:

使用not in运算符,我们检查items元组中是否不存在"bowl"

$ ./membership.py 
There is a coin in the tuple
There is no bowl in the tuple

这是示例的输出。

Python 三元运算符

三元运算符是一个简单的条件分配语句。

exp1 if condition else exp2

如果条件为true,则对exp1求值并返回结果。 如果条件为假,则求值exp2并返回其结果。

ternary.py

#!/usr/bin/env python

# ternary.py

age = 31

adult = True if age >= 18 else False

print("Adult: {0}".format(adult))

在许多国家,成年取决于您的年龄。 如果您的年龄超过特定年龄,则您已经成年。 对于三元运算符,这是一种情况。

adult = True if age >= 18 else False

首先求值条件。 如果年龄大于或等于 18,则返回True。 如果不是,则返回else关键字后面的值。 然后,将返回的值分配给adult变量。

$ ./ternary.py 
Adult: True

31 岁的成年人是成年人。

Python 按位运算符

小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制或十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。 我们有二进制逻辑运算符和移位运算符。 在 Python 等高级语言中很少使用按位运算符。

符号 含义
~ 按位取反
^ 按位异或
& 按位与
| 按位或
<< 左移
>> 右移

按位取反运算符分别将 1 更改为 0,将 0 更改为 1。

>>> ~7
-8
>>> ~-8
7

运算符恢复数字 7 的所有位。这些位之一还确定数字是否为负。 如果我们再一次对所有位取反,我们将再次得到 7。

按位,运算符在两个数字之间进行逐位比较。 仅当操作数中的两个对应位均为 1 时,位位置的结果才为 1。

     00110
  &  00011
   = 00010

第一个数字是二进制表示法 6,第二个数字是 3,最终结果是 2。

>>> 6 & 3
2
>>> 3 & 6
2

按位或运算符在两个数字之间进行逐位比较。 如果操作数中的任何对应位为 1,则位位置的结果为 1。

     00110
  |  00011
   = 00111

结果为00110或十进制 7。

>>> 6 | 3
7

按位互斥或运算符在两个数字之间进行逐位比较。 如果操作数中对应位中的一个或另一个(但不是全部)为 1,则位位置的结果为 1。

     00110
  ^  00011
   = 00101

结果为00101或十进制 5。

>>> 6 ^ 3
5

如前所述,Python 和其他高级语言很少使用按位运算符。 但是,在某些情况下会使用它们。 一个示例是掩码。 掩码是特定的位模式。 它确定是否设置了某些属性。

让我们举一个 GUI 编程的例子。

bitwise_or.py

#!/usr/bin/env python

# bitwise_or.py

import wx

app = wx.App()
window = wx.Frame(None, style=wx.MAXIMIZE_BOX | wx.RESIZE_BORDER 
	| wx.SYSTEM_MENU | wx.CAPTION |	 wx.CLOSE_BOX)
window.Show(True)

app.MainLoop()

这是 wxPython 代码的一个小示例。 wx.MAXIMIZE_BOXwx.RESIZE_BORDERwx.SYSTEM_MENUwx.CAPTIONwx.CLOSE_BOX是常数。 按位或运算符将所有这些常数添加到掩码中。 在我们的例子中,所有这些属性都是使用按位或运算符设置的,并应用于wx.Frame小部件。

最后,我们还有按位移位运算符。 按位移位运算符向右或向左移位。

number << n : multiply number 2 to the nth power
number >> n : divide number by 2 to the nth power

这些运算符也称为算术移位。

     00110
  >> 00001
   = 00011

我们将数字 6 的每个位向右移动。 等于将 6 除以 2。结果为00011或十进制 3。

>>> 6 >> 1
3

     00110
  << 00001
   = 01100

我们将数字 6 的每个位向左移动。 等于将数字 6 乘以 2。结果为01100或十进制 12。

>>> 6 << 1
12

Python 复合赋值运算符

复合赋值运算符由两个运算符组成。 他们是速记员。

>>> i = 1
>>> i = i + 1
>>> i
2
>>> i += 1
>>> i
3

+=复合运算符是这些速记运算符之一。

其他复合运算符是:

-=   *=   /=   //=   %=   **=   &=   |=   ^=   >>=   <<=

Python 运算符优先级

运算符优先级告诉我们首先求值哪个运算符。 优先级对于避免表达式中的歧义是必要的。

以下表达式 28 或 40 的结果是什么?

3 + 5 * 5

像数学中一样,乘法运算符的优先级高于加法运算符。 结果是 28。

(3 + 5) * 5

要更改求值顺序,可以使用方括号。 方括号内的表达式始终首先被求值。

以下列表显示了 Python 中的运算符优先级。

unary +  -  ~
**
*  /  %
+  -
>>  <<
&
^
|
<  <=  ==  >=  >  !=  is
not
and 
or

同一行上的运算符具有相同的优先级。 优先级从低到高。

precedence.py

#!/usr/bin/env python

# precedence.py

print(3 + 5 * 5)
print((3 + 5) * 5)

print(2 ** 3 * 5)
print(not True or True)
print(not (True or True))

在此代码示例中,我们显示一些常见的表达式。 每个表达式的结果取决于优先级。

print(2 ** 3 * 5)

幂运算符的优先级高于乘法运算符。 首先,对2 ** 3求值,返回 8。然后将结果乘以 5,结果为 40。

print(not True or True)

在这种情况下,not运算符具有更高的优先级。 首先,将第一个True值取反为False,然后or运算符组合FalseTrue,最后得到True

$ ./precedence.py 
28
40
40
True
False

关系运算符的优先级高于逻辑运算符。

positive.py

#!/usr/bin/env python

# positive.py

a = 1
b = 2

if (a > 0 and b > 0):
   print("a and b are positive integers")

and运算符等待两个布尔值。 如果其中一个操作数不是布尔值,则会出现语法错误。 在 Python 中,关系运算符在逻辑与之前进行求值。

$ ./positive.py 
a and b are positive integers

Python 关联规则

有时,优先级不能令人满意地确定表达式的结果。 还有另一个规则称为关联性。 运算符的关联性确定优先级与相同的运算符的求值顺序。

9 / 3 * 3

此表达式的结果是 9 还是 1? 乘法,删除和模运算符从左到右关联。 因此,该表达式的计算方式为:(9 / 3) * 3,结果为 9。

算术,布尔,关系和按位运算符都是从左到右关联的。

另一方面,赋值运算符是正确关联的。

>>> a = b = c = d = 0
>>> a, b, c, d
(0, 0, 0, 0)

如果关联从左到右,则以前的表达式将不可能。

复合赋值运算符从右到左关联。

>>> j = 0
>>> j *= 3 + 1
>>> j
0

您可能期望结果为 1。但是实际结果为 0。由于有关联性。 首先求值右边的表达式,然后应用复合赋值运算符。

在本章中,我们讨论了 Python 中的运算符。

Python 关键字

原文: http://zetcode.com/lang/python/keywords/

在 Python 编程教程的这一部分中,我们介绍了 Python 语言中的关键字。

Python 关键字

Python 关键字是构成 Python 语言词汇的特殊单词。 它是保留字,不能用作标识符。

Python 关键字列表

以下是 Python 编程语言的关键字列表。

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not
class               from                or
continue            global              pass

Python 是一种动态语言。 它随时间变化。 关键字列表将来可能会更改。

keywords.py

#!/usr/bin/env python

# keywords.py

import sys
import keyword

print("Python version: ", sys.version_info)
print("Python keywords: ", keyword.kwlist)

该脚本打印 Python 的版本及其实际的关键字列表。

$ ./keywords.py
Python version:  sys.version_info(major=3, minor=5, micro=2, releaselevel='final', serial=0)
Python keywords:  ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class',
'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global',
'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
'try', 'while', 'with', 'yield']

输出显示了 Python 3.5.2 的 Python 关键字列表。

Python 控制流程

while关键字是用于控制程序流程的基本关键字。 执行while循环内的语句,直到表达式的值为False为止。

while_kwd.py

#!/usr/bin/env python

# while_kwd.py

numbers = [22, 34, 12, 32, 4]
mysum = 0

i = len(numbers)

while i != 0:

   i -= 1
   mysum = mysum + numbers[i]

print("The sum is:", mysum)

在我们的脚本中,我们要计算数字列表中所有值的总和。 我们利用while循环。 我们确定列表的长度。 反复执行while循环,直到i等于零为止。 在循环的主体中,我们递减计数器并计算值的总和。

$ ./while_kwd.py
The sum is: 104

值的总和为 104。

如果需要,可使用break关键字来中断循环。

break_kwd.py

#!/usr/bin/env python

# break_kwd.py

import random

while True:

    val = random.randint(1, 30)
    print(val, end=" ")

    if val == 22:
        break

print()

在我们的示例中,我们打印随机整数。 如果数字等于 22,则用break关键字中断循环。

$ ./break_kwd.py
14 14 30 16 16 20 23 15 17 22

下一个示例显示continue关键字。 它用于中断当前周期,而不会跳出整个周期。 它启动一个新的循环。

continue_kwd.py

#!/usr/bin/env python

# continue_kwd.py

num = 0

while num < 1000:

      num = num + 1

      if num % 2 == 0:
         continue

      print(num, end=" ")

在示例中,我们打印了所有小于 1000 的数字,这些数字不能除以 2 而没有余数。

if关键字是常见的控制流关键字。 它用于确定要执行的语句。

if_kwd.py

#!/usr/bin/env python

# if_kwd.py

age = 17

if age > 18:

    print("Driving licence issued")
else:

    print("Driving licence not permitted")

if关键字测试此人是否大于 18 岁。如果符合条件,则颁发驾驶执照。 else关键字是可选的。 除非条件为True,否则执行else关键字之后的语句。

接下来,我们了解如何使用elif关键字合并语句。

elif_kwd.py

#!/usr/bin/env python

# elif_kwd.py

name = "Luke"

if name == "Jack":
    print("Hello Jack!")

elif name == "John":
    print("Hello John!")

elif name == "Luke":
    print("Hello Luke!")

else:
    print("Hello there!")

如果第一个测试的求值结果为False,那么我们继续下一个测试。 如果没有一个测试是True,则执行else语句。

$ ./elif_kwd.py
Hello Luke!

这是输出。

for关键字用于遍历集合中的项目,以便它们出现在容器中。

for_kwd.py

#!/usr/bin/env python

# for_kwd.py

lyrics = """\
Are you really here or am I dreaming
I can't tell dreams from truth
for it's been so long since I have seen you
I can hardly remember your face anymore
"""

for i in lyrics:

    print(i, end=" ")

在该示例中,我们有一个lyrics变量,该变量具有歌曲的节奏。 我们遍历文本并逐个字符打印文本。 print语句中的逗号阻止将每个字符打印在新行上。

$ ./for_kwd.py
A r e   y o u   r e a l l y   h e r e   o r   a m   I   d r e a m i n g
I   c a n ' t   t e l l   d r e a m s   f r o m   t r u t h
f o r   i t ' s   b e e n   s o   l o n g   s i n c e   I   h a v e   s e e n   y o u
I   c a n   h a r d l y   r e m e m b e r   y o u r   f a c e   a n y m o r e

这是脚本的输出。

Python 布尔表达式

首先,我们介绍适用于布尔值和表达式的关键字:isorandnot

objects.py

#!/usr/bin/env python

# objects.py

print(None == None)
print(None is None)

print(True is True)

print([] == [])
print([] is [])

print("Python" is "Python")

==运算符测试是否相等。 is关键字测试对象身份。 我们是否在谈论同一对象。 请注意,多个变量可能引用同一对象。

$ ./objects.py
True
True
True
True
False
True

输出可能会让您感到惊讶。 在 Python 语言中,只有一个None和一个True对象。 这就是为什么True相等并且也与True相同的原因。 无论如何,那里只有一个真理。 空列表[]等于另一个空列表[]; 但它们并不相同。 Python 已将它们放入两个不同的内存位置。 它们是两个不同的对象。 因此,is关键字返回False。 另一方面,"Python" is "Python"返回True。 这是由于优化。 如果两个字符串字面值相等,则将它们放在相同的内存位置。 字符串是不可变的实体,因此不会造成任何伤害。

not关键字取反布尔值。

not_kwd.py

#!/usr/bin/env python

# not_kwd.py

grades = ["A", "B", "C", "D", "E", "F"]

grade = "L"

if grade not in grades:
    print("unknown grade")

在我们的示例中,我们测试了等级值是否来自可能等级的列表。

$ ./not_kwd.py
unknown grade

如果必须满足布尔表达式中的所有条件,则使用关键字and

and_kwd.py

#!/usr/bin/env python

# and_kwd.py

sex = "M"
age = 26

if age < 55 and sex == "M":
    print("a young male")

在我们的示例中,我们测试是否满足两个条件。 如果变量age小于55且变量sex等于"M",则将"a young male"字符串打印到控制台。

$ ./and_kwd.py
a young male

如果必须满足至少一个条件,则使用关键字or

or_kwd.py

#!/usr/bin/env python

# or_kwd.py

name = "Jack"

if (name == "Robert" or name == "Frank" or name == "Jack"
      or name == "George" or name == "Luke"):
    print("This is a male")

如果至少一个表达式为true,则执行print语句。

当我们使用 Python 编程语言中的和/或关键字工作时,就会进行短路求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时,才求值第二个参数:当和的第一个参数求值为false时,总值必须为false; 当或的第一个参数为true时,总值必须为true

一个典型的例子如下。

short_circuit.py

#!/usr/bin/env python

# short_circuit.py

x = 10
y = 0

if (y != 0 and x/y < 100):
    print("a small value")

表达式的第一部分计算为false。 表达式的第二部分不计算。 否则,我们将得到ZeroDivisionError

Python 模块

以下关键字与模块一起使用。 模块是用于组织 Python 代码的文件。

import关键字用于将其他模块导入 Python 脚本。

import_kwd.py

#!/usr/bin/env python

# import_kwd.py

import math

print(math.pi)

我们使用import关键字将math模块导入脚本的名称空间。 我们打印PI值。

如果我们想给模块一个不同的别名,我们使用as关键字。

as_kwd.py

#!/usr/bin/env python

# as_kwd.py

import random as rnd

for i in range(10):
    print (rnd.randint(1, 10), end=" ")

print()

在这种情况下,我们将导入随机模块。 我们打印十个随机整数。 我们给随机模块一个不同的别名,即rnd。 在脚本中,我们使用新别名引用模块。

$ ./as_kwd.py
1 2 5 10 10 8 2 9 7 2

from关键字用于从模块中导入特定的变量,类或函数。

from_kwd.py

#!/usr/bin/env python

# from_kwd.py

from sys import version

print(version)

sys模块中,导入version变量。 如果要打印,则不需要使用模块名称。 版本变量被直接导入到我们的命名空间中,我们可以直接引用它。

$ ./from_kwd.py
3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)]

Python 函数

在这里,我们描述与函数关联的关键字。 def关键字用于创建新的用户定义函数。 函数是我们在其中组织代码的对象。

def_kwd.py

#!/usr/bin/env python

# def_kwd.py

def root(x):

    return x * x

a = root(2)
b = root(15)

print(a, b)

该示例演示了一个新的简单函数。 该函数计算数字的平方。 return键与函数定义紧密联系; 它退出函数并返回一个值。 然后将该值分配给ab变量。

lambda关键字创建一个新的匿名函数。 匿名函数未绑定到特定名称。

lambda_kwd.py

#!/usr/bin/env python

# lambda_kwd.py

a = lambda x: x * x

for i in (1, 2, 3, 4, 5):

    print(a(i), end=" ")

print()

如您在前面的示例中看到的,我们没有使用def关键字创建新函数。 取而代之的是,我们动态使用内联函数。

$ ./lambda_kwd.py
1 4 9 16 25

如果要访问在函数外部定义的变量,请使用global关键字。

global_kwd.py

#!/usr/bin/env python

# global_kwd.py

x = 15

def function():

    global x
    x = 45

function()
print(x)

通常,在函数内部分配x变量时,我们会创建一个新的局部变量,该局部变量仅在该函数中有效。 但是,如果使用global关键字,则会在函数定义中更改变量ouside

$ ./global_kwd.py
45

Python 异常

接下来,我们将使用与异常处理一起使用的关键字。

$ cat films
Fargo
Aguirre, der Zorn Gottes
Capote
Grizzly man
Notes on a scandal

这是一个文件,其中包含一些电影标题。 在代码示例中,我们将阅读它。

try_except_finally.py

#!/usr/bin/env python

# try_except_finally.py

f = None

try:

    f = open('films', 'r')

    for i in f:

        print(i, end="")

except IOError:

    print("Error reading file")

finally:

    if f:
        f.close()

我们尝试读取电影文件。 如果没有异常,我们将文件的内容打印到控制台。 可能会有异常。 例如,如果我们提供了错误的文件名。 在这种情况下,会引发IOError异常。 except关键字捕获异常并执行其代码块。 最后始终执行finally关键字。 我们用它来清理我们的资源。

在下一个示例中,我们显示如何使用raise关键字创建用户定义的异常。

raise_kwd.py

#!/usr/bin/env python

# raise_kwd.py

class YesNoException(Exception):

   def __init__(self):

       print('This is not a yes or no answer')

answer = 'y'

if (answer != 'yes' and answer != 'no'):
    raise YesNoException

else:
    print('Correct value')

在该示例中,我们仅期望是/否值。 对于其他可能性,我们提出一个异常。

$ ./raise_kwd.py
This is not a yes or no answer
Traceback (most recent call last):
  File "./raise_kwd.py", line 15, in <module>
    raise YesNoException
__main__.YesNoException

其他关键词

del关键字删除对象。

del_kwd.py

#!/usr/bin/env python

# del_kwd.py

a = [1, 2, 3, 4]

print(a)
del a[:2]
print(a)

在我们的示例中,我们有四个整数的列表。 我们从列表中删除第一个数字。 结果将打印到控制台。

$ ./del_kwd.py
[1, 2, 3, 4]
[3, 4]

这是示例的输出。

pass关键字不执行任何操作。 在某些情况下,这是一个非常方便的关键字。

def function():
    pass

我们有一个函数。 此函数尚未实现。 (将在以后。)函数的主体不能为空。 因此,我们可以在此处使用pass关键字,而不是打印“函数尚未实现”之类的内容。

assert关键字用于调试目的。 我们可以将其用于对我们显而易见的测试条件。 例如,我们有一个计算工资的程序。 我们知道薪水不能少于零。 因此,我们可以在代码中添加这样的断言。 如果断言失败,则解释器抱怨。

assert_kwd.py

#!/usr/bin/env python

# assert_kwd.py

salary = 3500
salary -= 3560 # a mistake was done

assert salary > 0

在程序执行期间,犯了一个错误。 薪水变成负数。

$ ./assert_kwd.py
Traceback (most recent call last):
  File "./assert_kwd.py", line 8, in <module>
    assert salary > 0
AssertionError

AssertionError无法执行脚本。

class关键字用于创建新的用户定义对象。

class_kwd.py

#!/usr/bin/env python

# class_kwd.py

class Square:

    def __init__(self, x):
        self.a = x

    def area(self):
        print(self.a * self.a)

sq = Square(12)
sq.area()

在代码示例中,我们创建一个新的Square类。 然后,我们实例化该类并创建一个对象。 我们计算方形物体的面积。

exec关键字动态执行 Python 代码。

exec_kwd.py

#!/usr/bin/env python

# exec_kwd.py

exec("for i in [1, 2, 3, 4, 5]: print(i, end=' ')")

我们使用for循环从列表中打印五个数字; 全部都在exec关键字内。

$ ./exec_kwd.py
1 2 3 4 5

这是示例输出。

接下来,我们提到in关键字。 关键字测试序列中是否存在值。

in_kwd.py

#!/usr/bin/env python

# in_kwd.py

print(4 in (2, 3, 5, 6))

for i in range(25):
    print(i, end=" ")

print()

在此示例中,in关键字测试数字 4 是否在元组中。 第二种用法是在for循环中遍历元组。 内置函数range()返回整数0..24

$ ./in_kwd.py
False
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

yield关键字与生成器一起使用。

yield_kwd.py

#!/usr/bin/env python

# yield_kwd.py

def gen():

    x = 11
    yield x

it = gen()

print(it.__next__())

yield关键字退出生成器并返回一个值。

$ ./yield_kwd.py
11

在 Python 教程的这一部分中,我们介绍了 Python 关键字。

Python 函数

原文: http://zetcode.com/lang/python/functions/

在 Python 编程教程的这一部分中,我们介绍了 Python 中的函数。

Python 函数定义

函数是用于执行特定操作的可重用代码块。 使用函数的优点是:

  • 减少代码重复
  • 将复杂的问题分解成更简单的部分
  • 提高代码的清晰度
  • 重用代码
  • 信息隐藏

Python 中的函数是一等公民。 这意味着函数与 Python 中的其他对象具有同等的状态。 可以将函数分配给变量,存储在集合中或作为参数传递。 这给语言带来了额外的灵活性。

Python 函数类型

函数有两种基本类型:内置函数和用户定义函数。 内置函数是 Python 语言的一部分; 例如dir()len()abs()。 用户定义的函数是使用def关键字创建的函数。

Python 创建函数

使用def关键字创建一个函数。 函数块中的语句必须缩进。

def function():
    pass

def关键字后跟带有圆括号和冒号的函数名称。 缩进语句形成函数的主体。

该函数稍后在需要时执行。 我们说我们调用函数。 如果我们调用一个函数,则会执行函数体内的语句。 在调用函数之前,它们不会执行。

myfunc()

要调用函数,我们用圆括号指定函数名称。

ret.py

#!/usr/bin/env python

"""
The ret.py script shows how to work with
functions in Python.
Author: Jan Bodnar
ZetCode, 2019
"""

def show_module_name():

    print(__doc__)

def get_module_file():

    return __file__

a = show_module_name()
b = get_module_file()

print(a, b)

脚本顶部的字符串称为文档字符串。 它记录了当前脚本。 我们放入 Python 代码的文件称为模块。

我们定义两个函数。 第一个函数打印模块文档字符串。 第二个返回模块的路径。 函数可能会或可能不会返回值。 如果函数没有返回值,则它隐式返回None__doc____file__是特殊的状态属性。 请注意,属性的两侧都有两个下划线。

$ ./ret.py

The ret.py script shows how to work with
functions in Python.
Author: Jan Bodnar
ZetCode, 2019

None C:/Users/Jano/PycharmProjects/Simple/simple.py

这是程序的输出。

函数的定义必须先于其用法。 否则,口译员会抱怨NameError

func_prec.py

#!/usr/bin/env python

# func_prec.py

def f1():
    print("f1()")

f1()
#f2()

def f2():
    print("f2()")

在上面的示例中,我们有两个函数定义。 一行被注释。 函数调用不能超出其定义。

#f2()

def f2():
    print("f2()")

仅在定义后才能调用f2()。 取消注释该行,我们得到一个NameError

在哪里定义函数

可以在模块,类或其他函数中定义函数。 在类内部定义的函数称为方法。

defining.py

#!/usr/bin/env python

# defining.py

class Some():

    @staticmethod
    def f():
        print("f() method")

def f():
    print("f() function")

def g():
    def f():
        print("f() inner function")
    f()

Some.f()
f()
g()

在此示例中,我们在三个不同的位置定义了f()函数。

# defining.py

class Some():

    @staticmethod
    def f():
        print("f() method")

静态方法在Some类中用装饰器定义。

def f():
    print("f() function")

该函数在模块中定义。

def g():
    def f():
        print("f() inner function")
    f()

此处,f()函数在另一个g()函数内部定义。 这是一个内部函数。

Some.f()
f()
g()

通过使用方括号指定类名称,点运算符和函数名称来调用静态方法。 其他函数使用其名称和方括号来调用。

$ ./defining.py
f() method
f() function
f() inner function

这是输出。

Python 函数是对象

Python 中的函数是对象。 它们可以像 Python 中的其他对象一样进行操作。 因此,职能被称为头等公民。 在其他 OOP 语言(例如 Java 或 C# )中,情况并非如此。

fun_obj.py

#!/usr/bin/env python

# fun_obj.py

def f():
    """This function prints a message """

    print("Today it is a cloudy day")

print(isinstance(f, object))
print(id(f))

print(f.__doc__)
print(f.__name__)

在此脚本中,我们表明我们的函数也是一个对象。

def f():
    """This function prints a message """

    print("Today it is a cloudy day")

我们定义一个f()函数。 它将消息打印到控制台。 它还具有一个文档字符串。

print(isinstance(f, object))

isinstance()函数检查f()函数是否是object的实例。 Python 中的所有对象均从该基本实体继承。

print(id(f))

Python 中的每个对象都有一个唯一的 ID。 id()函数返回对象的 ID。

print(f.__doc__)
print(f.__name__)

对象可能具有属性; 我们打印函数的两个属性:__doc____name__

$ ./fun_obj.py
True
140353774014536
This function prints a message
f

这是程序的输出。

函数可以存储在集合中并传递给其他函数。

fun_coll.py

#!/usr/bin/env python

# fun_coll.py

def f():
    pass

def g():
    pass

def h(f):
    print(id(f))

a = (f, g, h)

for i in a:
    print(i)

h(f)
h(g)

我们定义了三个函数。 我们将它们放在一个元组中,然后将它们传递给函数。

a = (f, g, h)

for i in a:
    print(i)

我们将三个函数对象放在一个元组中,并使用for循环遍历它。

h(f)
h(g)

我们将f()g()函数传递给h()函数。

$ ./fun_coll.py
<function f at 0x0000015B998E9D08>
<function g at 0x0000015B998E9E18>
<function h at 0x0000015B998E9840>
1492929912072
1492929912344

这是fun_coll.py程序的输出。

Python 中的三种函数

从特定的角度来看,我们可以辨别出三种函数。 始终可供使用的函数,必须导入的外部模块中包含的函数以及由程序员使用def关键字定义的函数。

three_kinds.py

#!/usr/bin/env python

from math import sqrt

def cube(x):
    return x * x * x

print(abs(-1))
print(cube(9))
print(sqrt(81))

上面的代码中存在三种函数。

from math import sqrt

sqrt()函数是从数学模块导入的。

def cube(x):
    return x * x * x

cube()函数是一个自定义函数。

print(abs(-1))

abs()函数是易于访问的内置函数。 它是语言核心的一部分。

Python return关键字

创建一个函数来执行特定任务。 通常,这种任务会产生结果。 return关键字用于从函数返回值。 函数可能会也可能不会返回值。 如果一个函数没有return关键字,它将发送None

returning.py

#!/usr/bin/env python

# returning.py

def show_message(msg):
    print(msg)

def cube(x):
    return x * x * x

x = cube(3)
print(x)

show_message("Computation finished.")
print(show_message("Ready."))

我们定义了两个函数。 一个使用return关键字,另一个则不使用。

def show_message(msg):
    print(msg)

show_message()函数不会显式返回值。 它在控制台上显示一条消息。

def cube(x):
    return x * x * x

cube()函数计算一个表达式,并使用return关键字返回其结果。

x = cube(3)

在这一行中,我们称为cube()函数。 返回cube()函数的计算结果,并将其分配给x变量。 现在保存结果值。

show_message("Computation finished.")

我们以消息为参数调用show_message()函数。 该消息将打印到控制台。 我们不期望此函数有值。

print(show_message("Ready."))

此代码产生两行。 一种是通过show_message()函数打印的消息。 另一个是None值,该值由没有return语句的函数隐式发送。

$ ./returning.py
27
Computation finished.
Ready.
None

这是示例输出。

我们可以从函数中发送多个值。 return关键字之后的对象用逗号分隔。

returning2.py

#!/usr/bin/env python

# returning2.py

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

def stats(x):

    _mx = max(x)
    _mn = min(x)
    _ln = len(x)
    _sm = sum(x)

    return _mx, _mn, _ln, _sm

mx, mn, ln, sm = stats(n)
print(stats(n))

print(mx, mn, ln, sm)

stats()函数的定义。 此函数返回四个值。

return _mx, _mn, _ln, _sm

return关键字发回四个数字。 这些数字用逗号分隔。 实际上,我们已经发送了包含这四个值的元组。 我们也可以返回列表而不是元组。

mx, mn, ln, sm = stats(n)

返回的值分配给局部变量。

$ ./returning2.py
(5, 1, 5, 15)
5 1 5 15

这是输出。

Python 函数重新定义

Python 本质上是动态的。 可以重新定义已经定义的函数。

redefinition.py

#!/usr/bin/env python

# redefinition.py

from time import gmtime, strftime

def show_message(msg):
    print(msg)

show_message("Ready.")

def show_message(msg):
    print(strftime("%H:%M:%S", gmtime()))
    print(msg)

show_message("Processing.")

我们定义一个show_message()函数。 稍后,我们提供相同函数的新定义。

from time import gmtime, strftime

从时间模块中,我们导入两个函数,用于计算当前时间。

def show_message(msg):
    print(msg)

这是函数的第一个定义。 它仅将消息打印到控制台。

def show_message(msg):
    print(strftime("%H:%M:%S", gmtime()))
    print(msg)

在源代码的后面,我们设置了showMessage()函数的新定义。 该消息之前带有时间戳。

$ ./redefinition.py
Ready.
23:49:33 Processing.

这是输出。

Python 函数参数

大多数函数接受参数。 参数是发送到函数的值。 这些函数处理这些值并有选择地返回一些值。

fahrenheit.py

#!/usr/bin/env python

# fahrenheit.py

def C2F(c):
    return c * 9/5 + 32

print(C2F(100))
print(C2F(0))
print(C2F(30))

在我们的示例中,我们将摄氏温度转换为华氏温度。 C2F()函数接受一个参数c,即摄氏温度。

$ ./fahrenheit.py
212
32
86

Python 函数中的参数可能具有隐式值。 如果未提供任何值,则使用隐式值。

fun_implicit.py

#!/usr/bin/env python

# fun_implicit.py

def power(x, y=2):

    r = 1

    for i in range(y):
        r = r * x

    return r

print(power(3))
print(power(3, 3))
print(power(5, 5))

在这里,我们创建了幂函数。 该函数有一个带有隐式值的参数。 我们可以使用一个或两个参数来调用该函数。

$ ./fun_implicit.py
9
27
3125

Python 函数可以使用关键字指定其参数。 这意味着在调用函数时,我们同时指定了关键字和值。 当我们有多个参数并且不使用关键字而使用它们时,传递这些参数的顺序至关重要。 如果我们期望在没有关键字的函数中使用名称,年龄或性别,则无法更改其顺序。 如果使用关键字,我们可以。

fun_keywords.py

#!/usr/bin/env python

# fun_keywords.py

def display(name, age, sex):

    print("Name: ", name)
    print("Age: ", age)
    print("Sex: ", sex)

display("Lary", 43, "M")
display("Joan", 24, "F")

在此示例中,我们指定参数的顺序很重要。 否则,我们将得到错误的结果。

$ ./fun_keywords.py
Name:  Lary
Age:  43
Sex:  M
Name:  Joan
Age:  24
Sex:  F

fun_keywords2.py

#!/usr/bin/env python

# fun_keywords2.py

def display(name, age, sex):

    print("Name: ", name)
    print("Age: ", age)
    print("Sex: ", sex)

display(age=43, name="Lary", sex="M")
display(name="Joan", age=24, sex="F")

现在我们用它们的关键字来调用函数。 可以更改顺序,尽管不建议这样做。 请注意,我们不能在关键字参数之后使用非关键字参数。 这将导致语法错误。

display("Joan", sex="F", age=24)

这是法律构想。 非关键字参数后可以跟关键字参数。

display(age=24, name="Joan", "F")

这将导致语法错误。 非关键字参数不能跟在关键字参数之后。

Python 中的函数可以接受任意数量的参数。

arbitrary_args.py

#!/usr/bin/env python

# arbitrary_args.py

def do_sum(*args):
    """Function returns the sum
of all values"""

    r = 0

    for i in args:
        r += i

    return r

print(do_sum.__doc__)
print(do_sum(1, 2, 3))
print(do_sum(1, 2, 3, 4, 5))

我们使用*运算符表示该函数接受任意数量的参数。 do_sum()函数返回所有参数的总和。 函数主体中的第一个字符串称为函数文档字符串。 用于记录函数。 该字符串必须用三引号引起来。

$ ./arbitrary_args.py
Function returns the sum
of all values
6
15

我们还可以在函数中使用**构造。 在这种情况下,该函数将接受字典。 字典有任意长度。 然后,我们通常可以照常解析字典。

details.py

#!/usr/bin/env python

# details.py

def display(**details):

    for i in details:
        print(f"{i}: {details[i]}")

display(name="Larry", age=43, sex="M")

本示例说明了这种情况。 我们可以提供任意数量的键值参数。 该函数将处理所有这些。

$ ./details.py
age: 43
name: Larry
sex: M

Python 通过引用传递参数

函数的参数通过引用传递。 一些语言将对象的副本传递给函数。 通过引用传递对象有两个重要结论:a)与传递对象副本相比,此过程更快。 b)在函数中修改的可变对象将永久更改。

passing_by_reference.py

#!/usr/bin/env python

# passing_by_reference.py

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

print("Original list:", n)

def f(x):

    x.pop()
    x.pop()
    x.insert(0, 0)
    print("Inside f():", x)

f(n)

print("After function call:", n)

在我们的示例中,我们将整数列表传递给函数。 该对象在函数体内被修改。 调用该函数(原始对象)后,将修改整数列表。

def f(x):

    x.pop()
    x.pop()
    x.insert(0, 0)
    print("Inside f():", x)

在函数主体中,我们使用原始对象。 不带对象的副本。 在许多编程语言中,默认情况下,我们将收到对象的副本。

$ ./passing_by_reference.py
Original list: [1, 2, 3, 4, 5]
Inside f(): [0, 1, 2, 3]
After function call: [0, 1, 2, 3]

一旦列表被修改,它就被永久修改了。

Python 全局和局部变量

接下来,我们将讨论如何在 Python 函数中使用变量。

local_variable.py

#!/usr/bin/env python

# local_variable.py

name = "Jack"

def f():
    name = "Robert"
    print("Within function", name)

print("Outside function", name)
f()

在函数体中定义的变量具有局部范围。 它仅在函数体内有效。

$ ./local_variable.py
Outside function Jack
Within function Robert

这是示例输出。

global_variable.py

#!/usr/bin/env python

# global_variable.py

name = "Jack"

def f():
    print("Within function", name)

print("Outside function", name)
f()

默认情况下,我们可以在函数体内获取全局变量的内容。

$ ./global_variable.py
Outside function Jack
Within function Jack

但是,如果要更改函数中的全局变量,则必须使用global关键字。

global_variable2.py

#!/usr/bin/env python

# global_variable2.py

name = "Jack"

def f():

    global name
    name = "Robert"
    print("Within function", name)

print("Outside function", name)
f()
print("Outside function", name)

现在,我们将在函数内部更改全局名称变量的内容。

global name
name = "Robert"

使用global关键字,我们引用在函数主体外部定义的变量。 该变量被赋予一个新值。

$ ./global_variable2.py
Outside function Jack
Within function Robert
Outside function Robert

Python 匿名函数

可以在 Python 中创建匿名函数。 匿名函数没有名称。 使用lambda关键字,几乎无法创建任何匿名函数。 Python 程序员也将匿名函数称为 lambda 函数。 它们是 Python 中合并的函数示例的一部分。

Lambda 函数仅限于单个表达式。 它们可以在可以使用常规函数的任何地方使用。 ZetCode 上有一个 Python lambda 函数教程。

lambda_fun.py

#!/usr/bin/env python

# lambda_fun.py

y = 6

z = lambda x: x * y
print(z(8))

这是 lambda 函数的一个小例子。

z = lambda x: x * y

lambda关键字创建一个匿名函数。 x是传递给 lambda 函数的参数。 参数后跟一个冒号。 冒号旁边的代码是在调用 lambda 函数时执行的表达式。 lambda 函数被分配给z变量。

print(z(8))

lambda 函数被执行。 数字 8 传递给匿名函数,结果返回 48。 请注意,z不是此函数的名称。 它只是分配了匿名函数的变量。

$ ./lambda_fun.py
48

这是示例的输出。

lambda 函数可以与map()filter()函数之类的 Python 语言的其他函数部件完美地结合使用。

lambda_fun2.py

#!/usr/bin/env python

# lambda_fun2.py

cs = [-10, 0, 15, 30, 40]

ft = map(lambda t: (9.0/5)*t + 32, cs)
print(list(ft))

在示例中,我们列出了摄氏温度。 我们创建一个包含华氏温度的新列表。

ft = map(lambda t: (9.0/5)*t + 32, cs)

map()函数将匿名函数应用于cs列表的每个元素。 它返回计算出的华氏温度的可迭代值。

$ ./lambda_fun2.py
[14.0, 32.0, 59.0, 86.0, 104.0]

这是示例输出。

本章是关于 Python 中的函数的。

Python 中的文件

原文: http://zetcode.com/lang/python/files/

在 Python 编程教程的这一部分中,我们处理文件以及标准输入和输出。 我们展示了如何从文件读取和写入文件。 我们简要介绍pickle模块。

Python 中的所有内容都是一个对象。 UNIX 中的所有内容都是文件。

Python open函数

open()函数用于在 Python 中打开文件。

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None)

file是要打开的文件的名称。 mode指示如何打开文件:用于读取,写入或附加。 buffering是用于设置缓冲策略的可选整数。 encoding是用于解码或编码文件的编码名称。 errors是一个可选的字符串,它指定如何处理编码和解码错误。 newline控制换行符的行为。

文件模式为:

模式 含义
'r' 读取(默认)
'w' 写入
'a' 附加
'b' 二进制数据
'+' 更新(读写)
'X' 互斥创建,如果文件存在则失败

在以下示例中,我们使用此文本文件。

works.txt

Lost Illusions
Beatrix
Honorine
The firm of Nucingen
Old Goriot
Colonel Chabert
Cousin Bette

Python with 语句

处理文件通常会导致错误; 因此,我们必须管理可能的异常。 另外,当不再需要文件对象时,必须关闭该文件对象。 with语句通过封装通用的准备和清除任务来简化异常处理。 它还会自动关闭打开的文件。

read_width.py

#!/usr/bin/env python

# read_width.py

with open('works.txt', 'r') as f:

    contents = f.read()
    print(contents)

该示例读取works.txt文件的内容。

with open('works.txt', 'r') as f:

通过open()函数,我们打开works.txt文件进行读取。 文件对象的别名为fwith语句负责处理异常和关闭打开的文件。

contents = f.read()

read()方法读取所有内容,直到 EOF。 它将数据作为一个大字符串返回。

Python 读取函数

read()函数从文件中读取指定数量的字节。 如果未指定字节数,它将读取整个文件。

read_data.py

#!/usr/bin/env python

# read_data.py

import sys

with open('works.txt', 'r') as f:

    print(f.read(5))
    print(f.read(9))

该代码示例从文件中读取了五个字母,并将它们打印到控制台。 然后读取并打印另外九个字母。 第二个操作继续从第一个操作结束的位置读取。

$ ./read_data.py
Lost
Illusions

这是输出。

Python 文件位置

文件位置是我们从中读取数据的文件位置。 tell()方法给出文件中的当前位置,seek()方法移动文件中的位置。

file_positions.py

#!/usr/bin/env python

# file_positions.py

with open('works.txt', 'r') as f:

    print(f.read(14))
    print(f"The current file position is {f.tell()}")

    f.seek(0, 0)

    print(f.read(30))

在示例中,我们读取 14 个字节,然后确定并打印当前文件位置。 我们将当前文件位置移到seek()的开头,再读取 30 个字节。

$ ./file_positions.py
Lost Illusions
The current file position is 14
Lost Illusions
Beatrix
Honorin

这是输出。

Python readline方法

readline()方法从文件读取一行。 字符串中保留尾随换行符。 函数到达文件末尾时,将返回一个空字符串。

readbyline.py

#!/usr/bin/env python

# readbyline.py

with open('works.txt', 'r') as f:

    while True:

        line = f.readline()

        if not line:
            break

        else:
            print(line.rstrip())

使用readline()方法和while循环,我们从文件中读取了所有行。

while True:

我们开始无止境的循环; 因此,我们稍后必须使用break语句跳过循环。

line = f.readline()

从文件中读取一行。

if not line:
    break

else:
    print(line.rstrip())

如果到达 EOF,则调用break语句; 否则,我们将该行打印到控制台,同时在该行的末尾删除换行符。

在下面的示例中,我们使用一种更方便的方式浏览文件的各行。

read_file.py

#!/usr/bin/env python

# read_file.py

with open('works.txt', 'r') as f:

    for line in f:

        print(line.rstrip())

在内部,文件对象是迭代器。 我们可以将文件对象传递给for循环来遍历它。

Python readlines方法

readlines()方法读取数据,直到文件结尾,然后返回行列表。

readlines.py

#!/usr/bin/env python

# readlines.py

with open('works.txt', 'r') as f:

    contents = f.readlines()

    for i in contents:

        print(i.strip())

readlines()方法将文件的所有内容读入内存。 但是,对于非常大的文件,这可能会出现问题。

Python write方法

write()方法将字符串写入文件。

strophe.py

#!/usr/bin/env python

# strophe.py

text = '''Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find\n'''

with open('strophe.txt', 'w') as f:

    f.write(text)

这次我们以"w"模式打开文件,以便我们可以对其进行写入。 如果该文件不存在,则会创建它。 如果存在,它将被覆盖。

$ cat strophe.txt
Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find

Python 标准 I/O

基本的 I/O 连接共有三种:标准输入,标准输出和标准错误。 标准输入是进入程序的数据。 标准输入来自键盘。 标准输出是我们使用 print 关键字打印数据的地方。 除非重定向,否则它是终端控制台。 标准错误是程序写入错误消息的流。 通常是文本终端。

Python 中的标准输入和输出是sys模块中的对象。

对象 描述
sys.stdin 标准输入
sys.stdout 标准输出
sys.stderr 标准错误

符合 UNIX 的哲学,标准 I/O 流是文件对象。

Python 标准输入

标准输入是进入程序的数据。

read_name.py

#!/usr/bin/env python

# read_name.py

import sys

print('Enter your name: ', end='')

name = ''

sys.stdout.flush()

while True:

    c = sys.stdin.read(1)

    if c == '\n':
        break

    name = name + c

print('Your name is:', name)

read()方法从标准输入读取一个字符。 在我们的示例中,系统提示您输入'Enter your name: '。 我们输入名称,然后按EnterEnter键生成new line字符:\n

$ ./read_name.py
Enter your name: Peter
Your name is: Peter

为了获得输入,我们可以使用更高级别的函数:input()raw_input()

如果提供了input()函数,则会打印提示并读取输入。

input_example.py

#!/usr/bin/env python

# input_example.py

data = input('Enter value: ')

print('You have entered:', data)

$ ./input_example.py
Enter value: Hello there
You have entered: Hello there

Python 标准输出

标准输出是我们打印数据的地方。

std_output.py

#!/usr/bin/env python

# std_output.py

import sys

sys.stdout.write('Honore de Balzac, Father Goriot\n')
sys.stdout.write('Honore de Balzac, Lost Illusions\n')

在示例中,我们将一些文本写入标准输出。 在我们的例子中,这是终端控制台。 我们使用write()方法。

$ ./stdout.py
Honore de Balzac, Father Goriot
Honore de Balzac, Lost Illusions

默认情况下,print函数将一些文本放入sys.stdout

print_fun.py

#!/usr/bin/env python

# print_fun.py

print('Honore de Balzac')
print('The Splendors and Miseries of Courtesans', 'Gobseck', 'Father Goriot', sep=":")

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

for e in vals:
    print(e, end=' ')

print()

在此示例中,我们使用sepend参数。 sep分隔打印的对象,end定义最后打印的内容。

$ ./print_fun.py
Honore de Balzac
The Splendors and Miseries of Courtesans:Gobseck:Father Goriot
1 2 3 4 5

这是输出。

可以使用print()函数写入文件。 print()函数包含一个file参数,该参数告诉我们在哪里打印数据。

print2file.py

#!/usr/bin/env python

# print2file.py

with open('works.txt', 'w') as f:

    print('Beatrix', file=f)
    print('Honorine', file=f)
    print('The firm of Nucingen', file=f)

我们打开一个文件,并在其中写入巴尔扎克的书的三个书名。 文件对象被赋予file参数。

$ cat works.txt
Beatrix
Honorine
The firm of Nucingen

Python 重定向

标准输出可以重定向。 在以下示例中,我们将标准输出重定向到常规文件。

redirect.py

#!/usr/bin/env python

# redirect.py

import sys

with open('output.txt', 'w') as f:

    sys.stdout = f

    print('Lucien')
    sys.stdout.write('Rastignac\n')
    sys.stdout.writelines(['Camusot\n', 'Collin\n'])

    sys.stdout = sys.__stdout__

    print('Bianchon')
    sys.stdout.write('Lambert\n')

redirect.py脚本中,我们将标准输出重定向到常规文件output.txt。 然后,我们恢复原始的标准输出。 std.output的原始值保存在特殊的sys.__stdout__变量中。

$ ./redirect.py
Bianchon
Lambert
$ cat output.txt
Lucien
Rastignac
Camusot
Collin

Python pickle模块

到目前为止,我们一直在处理简单的文本数据。 如果我们使用对象而不是简单的文本怎么办? 在这种情况下,我们可以使用pickle模块。 此模块序列化 Python 对象。 Python 对象将转换为字节流并写入文本文件。 此过程称为酸洗。 从文件读取并重建对象的逆操作称为反序列化或解腌。

pickle_ex.py

#!/usr/bin/env python

# pickle_ex.py

import pickle

class Person:

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

    def get_name(self):
        return self.name

    def get_age(self):
        return self.age

person = Person('Monica', 15)
print(person.get_name())
print(person.get_age())

with open('monica', 'wb') as f:
    pickle.dump(person, f)

with open('monica', 'rb') as f2:
    monica = pickle.load(f2)

print(monica.get_name())
print(monica.get_age())

在我们的脚本中,我们定义一个Person类。 我们创建一个人。 我们使用dump()方法腌制对象。 我们关闭文件,再次打开以进行读取,然后使用load()方法解开对象。

$ ./pickle_ex.py
Monica
15
Monica
15

在 Python 教程的这一部分中,我们将处理文件以及 Python 中的标准输入和输出。

Python 中的面向对象编程

原文: http://zetcode.com/lang/python/oop/

在 Python 教程的这一部分中,我们将讨论 Python 中的面向对象编程。

那里有三种广泛使用的编程示例:过程编程,函数编程和面向对象的编程。 Python 支持所有三种编程示例。

面向对象编程

面向对象编程(OOP)是一种使用对象及其相互作用设计应用和计算机程序的编程示例。

OOP 中有一些基本的编程概念:

  • 抽象
  • 多态
  • 封装
  • 继承

抽象通过建模适合该问题的类来简化复杂的现实。 多态是将运算符或函数以不同方式用于不同数据输入的过程。 封装对其他对象隐藏了类的实现细节。 继承是一种使用已经定义的类形成新类的方法。

Python 对象

Python 中的所有内容都是一个对象。 对象是 Python OOP 程序的基本构建块。

object_types.py

#!/usr/bin/env python

# object_types.py

import sys

def function():
    pass

print(type(1))
print(type(""))
print(type([]))
print(type({}))
print(type(()))
print(type(object))
print(type(function))
print(type(sys))

在此示例中,我们显示所有这些实体实际上都是对象。 type()函数返回指定对象的类型。

$ ./object_types.py 
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
<class 'type'>
<class 'function'>
<class 'module'>

整数,字符串,列表,字典,元组,函数和模块是 Python 对象。

Python class关键字

先前的对象都是 Python 编程语言的内置对象。 用户定义的对象是使用class关键字创建的。 该类是定义未来对象性质的蓝图。 从类中,我们构造实例。 实例是从特定类创建的特定对象。 例如,Huck可能是Dog类的实例。

first_object.py

#!/usr/bin/env python

# first_object.py

class First:
    pass

fr = First()

print(type(fr))
print(type(First))

这是我们的头等舱。 该类的主体暂时留空。 按照惯例,给类起一个以大写字母开头的名称。

class First:
    pass

在这里,我们定义First类。 请注意,默认情况下,所有类均从基object继承。

fr = First()

在这里,我们创建First类的新实例。 换句话说,我们实例化了First类。 fr是对我们新对象的引用。

$ ./first_object.py 
<class '__main__.First'>
<class 'type'>

在这里,我们看到frFirst类的实例对象。

在类内部,我们可以定义属性和方法。 属性是对象的特征。 例如,这可以是雇员的工资。 方法定义了我们可以对对象执行的操作。 一种方法可以定义取消帐户。 从技术上讲,属性是变量,方法是在类内部定义的函数。

Python 对象初始化

称为__init__()的特殊方法用于初始化对象。

object_initialization.py

#!/usr/bin/env python

# object_initialization.py

class Being:

    def __init__(self):
        print("Being is initialized")

Being()

我们有一个Being类。 创建对象后立即自动调用特殊方法__init__()

$ ./object_initialization.py 
Being is initialized

这是示例输出。

Python 对象属性

属性是对象的特征。 在__init__()方法中设置属性。

attributes.py

#!/usr/bin/env python

# attributes.py

class Cat:

    def __init__(self, name):

        self.name = name

missy = Cat('Missy')
lucky = Cat('Lucky')

print(missy.name)
print(lucky.name)

在此代码示例中,我们有一个Cat类。 创建对象后立即自动调用特殊方法__init__()

def __init__(self, name):

类定义中的每个方法都以对实例对象的引用开头。 按照惯例,它的名称为selfself名称没有什么特别的。 例如,我们可以这样命名。 第二个参数name是自变量。 该值在类初始化期间传递。

self.name = name

在这里,我们将属性传递给实例对象。

missy = Cat('Missy')
lucky = Cat('Lucky')

在这里,我们创建两个对象:猫MissyLucky。 参数的数量必须与类定义的__init__()方法相对应。 "Missy""Lucky"字符串成为__init__()方法的name参数。

print(missy.name)
print(lucky.name)

在这里,我们打印两个猫对象的属性。 一个类的每个实例可以有自己的属性。

$ ./attributes.py 
Missy
Lucky

可以动态分配属性,而不仅仅是在初始化过程中。 下一个示例对此进行了说明。

attributes_dynamic.py

#!/usr/bin/env python

# attributes_dynamic.py

class Person:
    pass

p = Person()
p.age = 24
p.name = "Peter"

print("{0} is {1} years old".format(p.name, p.age))

我们定义并创建一个空的Person类。

p.age = 24
p.name = "Peter"

在这里,我们动态创建两个属性:agename

$ ./attributes_dynamic.py 
24 is Peter years old

Python 类属性

到目前为止,我们一直在讨论实例属性。 在 Python 中,还有所谓的类对象属性。 类的所有实例的类对象属性都相同。

class_attribute.py

#!/usr/bin/env python

# class_attribute.py

class Cat:
    species = 'mammal'

    def __init__(self, name, age):

        self.name = name
        self.age = age

missy = Cat('Missy', 3)
lucky = Cat('Lucky', 5)

print(missy.name, missy.age)
print(lucky.name, lucky.age)

print(Cat.species)
print(missy.__class__.species)
print(lucky.__class__.species)

在我们的示例中,我们有两只具有特定nameage属性的猫。 两只猫都有一些共同之处。 小姐和幸运者都是哺乳动物。 这反映在类级别属性species中。 该属性是在类主体中的任何方法名称之外定义的。

print(Cat.species)
print(missy.__class__.species)

有两种方法可以访问类对象属性:通过Cat类的名称,或借助特殊的__class__属性。

$ ./class_attribute.py 
Missy 3
Lucky 5
mammal
mammal
mammal

Python 方法

方法是在类主体内定义的函数。 它们用于通过对象的属性执行操作。 在 OOP 范式的封装概念中,方法至关重要。 例如,我们的AccessDatabase类中可能有一个connect()方法。 我们无需知道方法连接如何准确地连接到数据库。 我们只知道它用于连接数据库。 这对于划分编程中的职责至关重要,尤其是在大型应用中。

methods.py

#!/usr/bin/env python

# methods.py

class Circle:

    pi = 3.141592

    def __init__(self, radius=1):
        self.radius = radius

    def area(self):
        return self.radius * self.radius * Circle.pi

    def setRadius(self, radius):
        self.radius = radius

    def getRadius(self):
        return self.radius

c = Circle()

c.setRadius(5)
print(c.getRadius())
print(c.area())

在代码示例中,我们有一个Circle类。 我们定义了三种新方法。

def area(self):
    return self.radius * self.radius * Circle.pi

area()方法返回圆的面积。

def setRadius(self, radius):
    self.radius = radius

setRadius()方法为radius属性设置新值。

def getRadius(self):
    return self.radius

getRadius()方法返回当前半径。

c.setRadius(5)

在实例对象上调用该方法。 c对象与类定义的self参数配对。 数字 5 与radius参数配对。

$ ./methods.py 
5
78.5398

在 Python 中,我们可以通过两种方式调用方法。 有有界的和无界的方法调用。

bound_unbound_methods.py

#!/usr/bin/env python

# bound_unbound_methods.py

class Methods:

    def __init__(self):
        self.name = 'Methods'

    def getName(self):
        return self.name

m = Methods()

print(m.getName())
print(Methods.getName(m))

在此示例中,我们演示了两个方法调用。

print(m.getName())

这是绑定的方法调用。 Python 解释器会自动将m实例与self参数配对。

print(Methods.getName(m))

这是无界的方法调用。 实例对象已显式提供给getName()方法。

$ ./bound_unbound_methods.py 
Methods
Methods

Python 继承

继承是一种使用已经定义的类形成新类的方法。 新形成的类称为派生的类,我们从中衍生的类称为基类。 继承的重要好处是代码重用和降低程序的复杂性。 派生类(后代)将覆盖或扩展基类(祖先)的功能。

inheritance.py

#!/usr/bin/env python

# inheritance.py

class Animal:

    def __init__(self):
        print("Animal created")

    def whoAmI(self):
        print("Animal")

    def eat(self):
        print("Eating")

class Dog(Animal):

    def __init__(self):
        super().__init__()

        print("Dog created")

    def whoAmI(self):
        print("Dog")

    def bark(self):
        print("Woof!")

d = Dog()
d.whoAmI()
d.eat()
d.bark()

在此示例中,我们有两个类:AnimalDogAnimal是基类,Dog是派生类。 派生类继承基类的功能。 通过eat()方法显示。 派生的类修改了基类的现有行为,如whoAmI()方法所示。 最后,派生类通过定义新的bark()方法来扩展基类的功能。

class Dog(Animal):

    def __init__(self):
        super().__init__()

        print("Dog created")

我们将祖先类放在子孙类名称之后的圆括号中。 如果派生类提供了自己的__init__()方法,并且我们想调用父构造器,则必须借助super函数显式调用基类__init__()方法。

$ ./inherit.py 
Animal created
Dog created
Dog
Eating
Woof!

Python 多态

多态是对不同的数据输入以不同方式使用运算符或函数的过程。 实际上,多态意味着如果类 B 从类 A 继承,则不必继承关于类 A 的所有内容; 它可以完成 A 类所做的某些事情。

basic_polymorphism.py

#!/usr/bin/env python

# basic_polymorphism.py

a = "alfa"
b = (1, 2, 3, 4)
c = ['o', 'm', 'e', 'g', 'a']

print(a[2])
print(b[1])
print(c[3])

Python 在内置类型中广泛使用了多态。 在这里,我们对三种不同的数据类型使用相同的索引运算符。

$ ./basic_polymorphism.py 
f
2
g

多态在处理继承时最常用。

polymorphism.py

#!/usr/bin/env python

# polymorphism.py

class Animal:

   def __init__(self, name=''):

      self.name = name

   def talk(self):

      pass

class Cat(Animal):

   def talk(self):

      print("Meow!")

class Dog(Animal):

   def talk(self):

      print("Woof!")

a = Animal()
a.talk()

c = Cat("Missy")
c.talk()

d = Dog("Rocky")
d.talk()

在这里,我们有两种:狗和猫。 两者都是动物。 Dog类和Cat类继承了Animal类。 它们具有talk()方法,可以为它们提供不同的输出。

$ ./polymorphism.py 
Meow!
Woof!

Python 特殊方法

Python 编程语言中的类可以使用特殊的方法名称来实现某些操作。 这些方法不是直接调用,而是通过特定的语言语法调用。 这类似于在 C++ 或 Ruby 中所谓的运算符重载。

special_methods.py

#!/usr/bin/env python

# special_methods.py

class Book:

    def __init__(self, title, author, pages):

        print("A book is created")

        self.title = title
        self.author = author
        self.pages = pages

    def __str__(self):

        return "Title:{0} , author:{1}, pages:{2} ".format(
            self.title, self.author, self.pages)

    def __len__(self):

        return self.pages

    def __del__(self):

        print("A book is destroyed")

book = Book("Inside Steve's Brain", "Leander Kahney", 304)

print(book)
print(len(book))
del book

在我们的代码示例中,我们有一个book类。 在这里,我们介绍四种特殊方法:__init__()__str__()__len__()__del__()

book = Book("Inside Steve's Brain", "Leander Kahney", 304)

在这里,我们称为__init__()方法。 该方法创建Book类的新实例。

print(book)

print关键字调用__str__()方法。 此方法应返回对象的非正式字符串表示形式。

print(len(book))

len()函数调用__len__()方法。 就我们而言,我们打印书的页数。

del book

del关键字删除一个对象。 它调用其__del__()方法。

在下一个示例中,我们实现一个向量类并演示对其的加法和减法运算。

vector.py

#!/usr/bin/env python

# vector.py

class Vector:

    def __init__(self, data):

        self.data = data

    def __str__(self):

        return repr(self.data)

    def __add__(self, other):

        data = []

        for j in range(len(self.data)):

            data.append(self.data[j] + other.data[j])

        return Vector(data)

    def __sub__(self, other):

        data = []

        for j in range(len(self.data)):

            data.append(self.data[j] - other.data[j])

        return Vector(data)

x = Vector([1, 2, 3])
y = Vector([3, 0, 2])

print(x + y)
print(y - x)

该示例介绍了__add____sub__方法。

def __add__(self, other):

    data = []

    for j in range(len(self.data)):

        data.append(self.data[j] + other.data[j])

    return Vector(data)

在这里,我们实现向量的加法运算。 当我们使用+运算符添加两个Vector对象时,将调用__add__()方法。 在这里,我们将各个向量的每个成员相加。

$ ./vector.py 
[4, 2, 5]
[2, -2, -1]

这是输出。

在 Python 教程的这一部分中,我们介绍了 Python 中的面向对象编程。

Python 模块

原文: http://zetcode.com/lang/python/modules/

在 Python 教程的这一部分中,我们将使用 Python 模块。 几个示例说明了如何创建和使用 Python 模块。

模块是一个包含 Python 代码的文件。 Python 模块具有.py扩展名。

可以使用以下方法管理 Python 代码:

  • 函数
  • 模组
  • 包装

Python 模块用于组织 Python 代码。 例如,与数据库相关的代码位于数据库模块内部,安全代码位于安全模块中,等等。较小的 Python 脚本可以具有一个模块。 但是较大的程序分为几个模块。 模块组合在一起形成包装。

Python 模块名称

模块名称是带有.py扩展名的文件名。 当我们有一个名为empty.py的文件时,模块名称为空。 __name__是一个变量,其中包含要引用的模块的名称。 当前模块,正在执行的模块(也称为主模块)有一个特殊名称:'__main__'。 使用此名称,可以从 Python 代码中引用它。

当前工作目录中有两个文件:empty.pytest_empty.py。 第二个模块是执行的主模块。 导入第一个模块。 使用import关键字导入模块。

empty.py

"""
An empty module
"""

这是empty.py模块。

test_empty.py

#!/usr/bin/env python

import empty
import sys

print(__name__)
print(empty.__name__)
print(sys.__name__)

在此代码示例中,我们导入两个模块:内置模块sys和一个自定义模块empty。 我们将模块的名称打印到控制台。

$ ./test_empty.py 
__main__
empty
sys

正在执行的模块的名称始终为'__main__'。 其他模块以文件名命名。 可以使用import关键字将模块导入其他模块。

Python 定位模块

导入模块时,解释器首先搜索具有该名称的内置模块。 如果找不到,它将在变量sys.path给定的目录列表中搜索。 sys.path是一个字符串列表,用于指定模块的搜索路径。 它由当前工作目录,在PYTHONPATH环境变量中指定的目录名称以及一些其他与安装有关的目录组成。 如果找不到该模块,则会引发ImportError

locating_modules.py

#!/usr/bin/env python

import sys
import textwrap

sp = sorted(sys.path)
dnames = ', '.join(sp)

print(textwrap.fill(dnames))

该脚本从sys.path变量打印所有目录。

import textwrap

textwrap模块用于轻松设置段落格式。

sp = sorted(sys.path)

我们从sys.path变量中检索目录列表并对它们进行排序。

dnames = ', '.join(sp)

我们从列表中取出一个字符串。

$ ./locating_modules.py 
/home/janbodnar/.local/lib/python3.5/site-packages,
/home/janbodnar/PycharmProjects/Simple,
/home/janbodnar/PycharmProjects/Simple, /usr/lib/python3.5,
/usr/lib/python3.5/lib-dynload, /usr/lib/python3.5/plat-x86_64-linux-
gnu, /usr/lib/python3/dist-packages, /usr/lib/python35.zip,
/usr/local/lib/python3.5/dist-packages

这是一个示例输出。

Python import关键字

import关键字可以以多种方式使用。

from module import *

此构造会将所有 Python 定义导入另一个模块的名称空间。 有一个例外。 不导入以下划线字符_开头的对象。 预期它们只能由导入的模块在内部使用。 不建议使用这种方式导入模块。

everything.py

#!/usr/bin/python

from math import *

print(cos(3))
print(pi)

此导入构造已从内置math模块导入了所有定义。 我们可以直接调用数学函数,而无需引用math模块。

$ ./everything.py 
-0.9899924966004454
3.141592653589793

使用此导入构造可能会导致名称空间污染。 我们可能有多个同名的对象,并且它们的定义可以被覆盖。

pollution.py

#!/usr/bin/env python

from math import *

pi = 3.14

print(cos(3))
print(pi)

该示例将在控制台上打印 3.14。 这可能不是我们想要的。 在大型项目中,命名空间污染可能变得至关重要。

未导入的 Python 对象

以下示例显示了未使用此import构造导入的定义。

names.py

#!/usr/bin/env python

"""
names is a test module
"""

_version = 1.0

names = ["Paul", "Frank", "Jessica", "Thomas", "Katherine"]

def show_names():

    for i in names:
       print(i)

def _show_version():

    print(_version)

这是names.py模块。

test_names.py

#!/usr/bin/env python

from names import *

print(locals())

show_names()

_version变量和_show_version()函数未导入到test_names模块中。 我们在命名空间中看不到它们。 locals()函数为我们提供了模块中所有可用的定义。

导入特定对象

使用fromimport关键字,可以仅导入某些对象。

from module import fun, var

此导入构造仅从模块导入特定对象。 这样,我们仅导入我们需要的定义。

import_specific.py

#!/usr/bin/python

from math import sin, pi

print(sin(3))
print(pi)

我们从math模块导入两个对象。 我们无法引用其他定义(例如cos函数)。

imnames.py

#!/usr/bin/python

from names import _version, _show_version

print(_version)
_show_version()

我们也可以导入下划线开头的定义。 但这是一个不好的做法。

$ ./imnames.py 
1.0
1.0

Python 导入模块

最后一种构造使用最广泛。

import module

它防止名称空间污染,并允许从模块访问所有定义。

impmod.py

#!/usr/bin/env python

import math

pi = 3.14

print(math.cos(3))
print(math.pi)
print(math.sin(3))
print(pi)

在这种情况下,我们通过模块名称引用定义。 如我们所见,我们可以使用两个pi变量。 我们的定义来自math模块。

$ ./impmod.py 
-0.9899924966004454
3.141592653589793
0.1411200080598672
3.14

Python 别名模块

我们可以使用as关键字为模块创建别名。

importas.py

#!/usr/bin/python

# importas.py

import math as m

print(m.pi)
print(m.cos(3))

我们可以更改引用模块的名称。 为此,我们使用as关键字。

$ ./importas.py 
3.14159265359
-0.9899924966

ImportError

如果无法导入模块,则会引发ImportError

importerror.py

#!/usr/bin/env python

try:
    import empty2
except ImportError as e:
    print('Failed to import:', e)

我们尚未创建empty2模块。 因此引发了异常。

$ ./importerror.py
Failed to import: No module named empty2

示例输出。

执行 Python 模块

模块可以导入其他模块,也可以执行。 模块作者经常创建测试套件来测试模块。 仅当模块作为脚本执行时,__name__属性等于'__main__'

我们将在斐波那契模块上对此进行演示。 斐波那契数是一个数字序列,其中每个数字都是其两个直接前辈的总和。

fibonacci.py

#!/usr/bin/env python

"""
A module containing the fibonacci
function.
"""

def fib(n):

    a, b = 0, 1

    while b < n:

        print(b, end=" ")
        (a, b) = (b, a + b)

# testing

if __name__ == '__main__':
    fib(500)

通常可以照常导入该模块。 该模块也可以执行。

$ ./fibonacci.py 
1 1 2 3 5 8 13 21 34 55 89 144 233 377

如果确实导入了fibonacci模块,则不会自动执行测试。

>>> import fibonacci as fib
>>> fib.fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

导入了fibonacci模块,并执行了fib()函数。

Python dir函数

内置的dir()函数提供了包含模块定义名称的字符串排序列表。

dirfun.py

#!/usr/bin/env python

"""
This is dirfun module 
"""

import math, sys

version = 1.0

names = ["Paul", "Frank", "Jessica", "Thomas", "Katherine"]

def show_names():

   for i in names:
      print(i)

print(dir())

在此模块中,我们导入两个系统模块。 我们定义一个变量,一个列表和一个函数。

print(dir())

dir()函数返回模块当前名称空间中所有可用的名称。

$ ./dirfun.py
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', 
'__package__', '__spec__', 'math', 'names', 'show_names', 'sys', 'version']

我们可以看到一些内置名称,例如'__file__''__name__',以及我们定义和导入的所有其他名称。

Python 全局函数

globals()函数返回代表当前全局​​名称空间的字典。 它是全局名称及其值的字典。 它是当前模块的字典。

globalsfun.py

#!/usr/bin/env python

import textwrap

version = 1.0

def myfun():
    pass

gl = globals()
gnames = ', '.join(gl)

print(textwrap.fill(gnames))

我们使用globals()函数来打印当前模块的所有全局名称。

$ ./globalsfun.py
textwrap, __package__, version, __builtins__, __name__, __spec__,
__doc__, gl, __cached__, myfun, __loader__, __file__

这些是当前模块的全局名称。

Python __module__属性

__module__类属性具有定义该类的模块的名称。

animals.py

"""
module animals
"""

class Cat:
    pass

class Dog:
    pass

这是animals.py文件的内容。 我们有两个类。

mclass.py

#!/usr/bin/env python

from animals import Cat

class Being:
    pass

b = Being()
print(b.__module__)

c = Cat()
print(c.__module__)

在此代码中,我们使用__module__属性。

from animals import Cat

animals模块,我们导入Cat类。

class Being:
    pass

在当前模块中,我们定义一个类Being

b = Being()
print(b.__module__)

创建Being类的实例。 我们打印其模块的名称。

c = Cat()
print(c.__module__)

我们从Cat类创建一个对象。 我们还将在定义模块的位置打印。

$ ./mclass.py
__main__
animals

当前模块的名称为'__main__'Cat's模块的名称是动物。

本章是关于 Python 中的模块的。

Python 中的包

原文: http://zetcode.com/lang/python/packages/

在 Python 编程教程的这一部分中,我们介绍了包。

Python 包是具有通用目的的模块的集合。 包目录必须有一个名为__init__.py的特殊文件。 (从 Python 3.3 开始,不再需要__init__.py来定义包目录。)Python 模块是单个 Python 文件。

当我们处理包含成百上千个模块的大型项目时,使用包至关重要。 例如,我们可以将所有与数据库相关的模块放在一个数据库包中,并将用户界面代码放在ui包中。

内置包可在预定义目录中使用; 例如 Debian Linux 上的/usr/lib/python3.5C:\Users\Jano\AppData\Local\Programs\Python\Python36-32\Lib\site-packages

第三方包已安装到预定义目录中,例如 Debian Linux 上的/usr/local/lib/python3.5/dist-packages或 Windows 上的C:\Users\Jano\AppData\Local\Programs\Python\Python36-32\libs

Python 包管理

Python 包由 Python 包管理器pip进行管理。

$ sudo pip3 install arrow

例如,使用上述命令安装了箭头库。

$ sudo pip3 uninstall arrow

要卸载箭头,我们使用上面的命令。

__init__.py的 Python 包

在第一个示例中,我们使用 Python 创建一个简单的包。

$ tree
.
├── mymath
│   ├── __init__.py
│   └── mfuns.py
└── myprog.py

在当前工作目录中,我们有一个mymath目录和一个myprog.py脚本。 mymath包含__init__.py文件,该文件将mymath目录标记为包目录。

mymath目录有两个文件:__init__.py文件使常量成为 Python 包目录,而mfuns.py是 Python 模块。

__init__.py

__init__.py为空白。 它可以包含一些代码,但也可以为空。

mfuns.py

def mycube(x):

   return x * x * x 

mfuns.py模块中,我们定义了cube()函数。

myprog.py

#!/usr/bin/env python

# myprog.py

from mymath.mfuns import mycube

print(mycube(3))

myprog.py程序中,我们从mymath.mfuns模块导入mycube函数。 模块名称和包名称用点字符分隔。

__init__.py中的 Python 导入功能

在下一个示例中,我们在__init__.py文件中有一些代码。

$ tree
.
├── mymath
│   ├── __init__.py
│   └── mfuns.py
└── myprog.py

我们具有相同的目录结构。

__init__.py

from .mfuns import mycube

__init__.py文件中,我们导入mycube函数。 结果,当我们从mymath包中引用mycube函数时,我们不必指定模块名称。

mfuns.py

def mycube(x):

   return x * x * x 

mfuns.py模块中,我们定义了cube()函数。

myprog.py

#!/usr/bin/env python

# myprog.py

from mymath import mycube

print(mycube(3))

myprog.py程序中,我们导入mycube函数。 这次我们省略了模块名称。

没有__init__.py的 Python 包

从 Python 3.3 开始,无需使用__init__.py文件就可以定义包目录。

read.py
constants/
    data.py 

在当前工作目录中,我们有一个constants目录和一个read.py脚本。

data.py

colours = ('yellow', 'blue', 'red', 'orange', 'brown')
names = ('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')

data.py模块有两个元组。

read.py

#!/usr/bin/env python

# read.py

from constants.data import colours
import constants.data as mydata

print(colours)
print(mydata.names)

read.py脚本中,我们导入元组并将其打印到终端。

$ ./read.py 
('yellow', 'blue', 'red', 'orange', 'brown')
('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')

Python arrow

arrow是用于在 Python 中处理日期和时间的第三方库。

$ ls /usr/local/lib/python3.5/dist-packages/arrow
api.py  arrow.py  factory.py  formatter.py  __init__.py  
locales.py  parser.py  __pycache__  util.py

该库安装在 Debian Linux 中dist-packages下的arrow目录中。 该库随pip包管理器一起安装。 如我们所见,该库是 Python 模块的集合。

Python 子包

我们还可以创建子包。 要访问子包,我们使用点运算符。

$ tree
.
├── constants
│ ├── __init__.py
│ ├── data.py
│ └── numbers
│     ├── __init__.py
│     └── myintegers.py
└── read.py

这是新的层次结构。 我们有一个称为数字的子包。

constants/__init__.py

from .data import names

这是constants目录中的__init__.py文件。 我们导入names元组。

constants/data.py

names = ('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')

这是constants目录中的data.py模块。 它包含names元组。

numbers/__init__.py

from .myintegers import myintegers

数字包中的__init__.py文件具有这一行。

numbers/myintegers.py

myintegers = (2, 3, 45, 6, 7, 8, 9)

整数模块定义了七个整数的元组。 该元组将通过read.py脚本进行访问。

read.py

#!/usr/bin/env python

# read.py

from constants import names
from constants.numbers import myintegers

print(names)
print(myintegers)

这是read.py程序。 我们从constants包中导入names元组,并从constants.numbers子包中导入myintegers元组。

$ ./read.py 
('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')
(2, 3, 45, 6, 7, 8, 9)

这是输出。

在本章中,我们介绍了 Python 包。

Python 异常

原文: http://zetcode.com/lang/python/exceptions/

在 Python 教程的这一部分中,我们讨论 Python 中的异常。

执行期间检测到的错误称为异常。 在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为防止这种情况,我们必须应对可能发生的所有可能的错误。 为此,我们可以使用异常处理。

在 Python 中捕捉异常

在 Python 中,我们具有以下语法来处理异常:

try:
   # do something

except ValueError:
   # handle ValueError exception

except (IndexError, ZeroDivisionError):
   # handle multiple exceptions
   # IndexError and ZeroDivisionError

except:
   # handle all other exceptions

finally:
   # cleanup resources

我们期望发生异常的代码写在try块中。 except关键字捕获程序中指定的或剩余的异常。 始终执行可选的finally块; 它用于清除资源,例如打开的文件或数据库连接。

ZeroDivisionError

除以零是不可能的。 如果我们尝试执行此操作,则会引发ZeroDivisionError并中断脚本。

注意:以下示例演示了异常在 Python 中的工作方式。 确保除数不为零而不是捕获ZeroDivisionError更直接。

zero_division.py

#!/usr/bin/env python

# zero_division.py

def input_numbers():

    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b

x, y = input_numbers()
print(f"{x} / {y} is {x/y}")

在此脚本中,我们从控制台获得两个数字。 我们将这两个数相除。 如果第二个数字为零,则会出现异常。

Enter first number:3
Enter second number:0
Traceback (most recent call last):
    File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 14, in <module>
    print(f"{x} / {y} is {x/y}")
ZeroDivisionError: float division by zero

我们可以通过两种方式处理此问题。

zero_division2.py

#!/usr/bin/env python

# zero_division2.py

def input_numbers():

    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b

x, y = input_numbers()

while True:

    if y != 0:

        print(f"{x} / {y} is {x/y}")
        break

    else:

        print("Cannot divide by zero")
        x, y = input_numbers()

首先,我们仅检查y的值不为零。 如果y值为零,我们将打印警告消息并再次重复输入周期。 这样我们就可以处理错误,并且脚本不会中断。

$ ./zero_division2.py
Enter first number:4
Enter second number:0
Cannot divide by zero
Enter first number:5
Enter second number:0
Cannot divide by zero
Enter first number:5
Enter second number:6
5.0 / 6.0 is 0.8333333333333334

另一种方法是使用异常。

zero_division3.py

#!/usr/bin/env python

# zerodivision3.py

def input_numbers():

    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b

x, y = input_numbers()

try:
    print(f"{x} / {y} is {x/y}")

except ZeroDivisionError:

    print("Cannot divide by zero")
    x, y = input_numbers()

我们将代码放置在try关键字之后我们期望发生异常的位置。 如果引发了except关键字,则捕获该异常。 异常类型在except关键字之后指定。

except ValueError:
    pass
except (IOError, OSError):
    pass

要处理更多的异常,我们可以使用除关键字之外的更多异常,也可以将异常名称放在元组中。

ValueError

当内置操作或函数接收到类型正确但值不合适的自变量时,会引发ValueError,并且无法通过更精确的异常描述这种情况。

value_error.py

#!/usr/bin/env python

# value_error.py

def read_age():

    age = int(input("Enter your age: "))

    if age < 0 or age > 130:
        raise ValueError("Invalid age")

    return age

try:
    val = read_age()
    print(f"Your age is {val}")

except ValueError as e:
    print(e)

在该示例中,我们有一个读取年龄作为用户输入的函数。 当用户提供不正确的值时,我们将引发ValueError异常。

if age < 0 or age > 130:
    raise ValueError("Invalid age")

return age

负年龄是没有道理的,在现代没有记录到年龄超过 130 岁的人。

Python 多个异常

可以在一个except子句中捕获多个异常。

multiple_exceptions.py

#!/usr/bin/env python

# multiple_exceptions.py

import os

try:

    os.mkdir('newdir')
    print('directory created')

    raise RuntimeError("Runtime error occurred")

except (FileExistsError, RuntimeError) as e:
    print(e)

该代码示例在一个except语句中捕获了两个异常:FileExistsErrorRuntimeError

os.mkdir('newdir')

使用os.mkdir()方法创建一个新目录。 如果目录已经存在,则触发FileExistsError

raise RuntimeError("Runtime error occurred")

我们使用raise关键字手动引发运行时异常。

Python 异常参数

异常可以具有关联的值,该值指示错误的详细原因。 该值放在as关键字之后。

exception_as.py

#!/usr/bin/env python

# exception_argument.py

try:
    a = (1, 2, 3, 4)
    print(a[5])

except IndexError as e:

    print(e)
    print("Class:", e.__class__)

从异常对象中,我们可以获取错误消息或类名。

$ ./exception_as.py
tuple index out of range
Class: <class 'IndexError'>

Python 异常层次结构

异常是按层次结构组织的,它们是所有异常的父级Exception

interrupt.py

#!/usr/bin/env python

# interrupt.py

try:
    while True:
       pass

except KeyboardInterrupt:

    print("Program interrupted")

脚本开始并不断循环。 如果按 Ctrl + C ,我们将中断循环。 在这里,我们捕获了KeyboardInterrupt异常。

Exception
  BaseException
    KeyboardInterrupt

这是KeyboardInterrupt异常的层次结构。

interrupt2.py

#!/usr/bin/env python

# interrupt2.py

try:
    while True:
        pass

except BaseException:

    print("Program interrupted")

这个例子也可以。 BaseException也捕获键盘中断; 除其他异常。 但是,这不是一个好习惯。 我们应该在except子句中捕获特定的异常。

Python 用户定义的异常

如果需要,我们可以创建自己的异常。 我们通过定义一个新的异常类来做到这一点。

user_defined.py

#!/usr/bin/env python

# user_defined.py

class BFoundEx(Exception):

    def __init__(self, value):
        self.par = value

    def __str__(self):
        return f"BFoundEx: b character found at position {self.par}"

string = "There are beautiful trees in the forest."

pos = 0

for i in string:

    try:

        if i == 'b':
            raise BFoundEx(pos)
        pos = pos + 1

    except BFoundEx as e:
        print(e)

在我们的代码示例中,我们创建了一个新的异常。 异常是从基类Exception派生的。 如果在字符串中发现字母b的任何出现,我们将raise作为异常。

$ ./user_defined.py
'BFoundEx: b character found at position 10'

清理

有一个finally关键字,始终会执行。 不管是否引发异常。 它通常用于清理程序中的资源。

cleanup.py

#!/usr/bin/env python

# cleanup.py

f = None

try:

    f = open('data.txt', 'r')
    contents = f.readlines()

    for line in contents:
        print(line.rstrip())

except IOError:

    print('Error opening file')

finally:

    if f:
        f.close()

在我们的示例中,我们尝试打开一个文件。 如果我们无法打开文件,则会弹出IOError。 如果我们打开文件,我们想关闭文件处理器。 为此,我们使用finally关键字。 在finally块中,我们检查文件是否已打开。 如果打开了,我们将其关闭。 当我们使用数据库时,这是一种常见的编程结构。 在那里,我们类似地清理打开的数据库连接。

栈跟踪

栈跟踪显示了引发未捕获的异常时的调用栈(至此为止已调用的函数栈)。 Python traceback模块提供了一个标准接口,用于提取,格式化和打印 Python 程序的栈跟踪。 它精确地模仿了 Python 解释器在打印栈跟踪时的行为。

stacktrace_ex.py

#!/usr/bin/env python

# stacktrace_ex.py

import traceback

def myfun():

    def myfun2():

        try:
            3 / 0

        except ZeroDivisionError as e:

            print(e)
            print("Class:", e.__class__)

            for line in traceback.format_stack():
                print(line.strip())

    myfun2()

def test():
    myfun()

test()

在示例中,我们在嵌套的myfun2函数中将除以零的异常。

for line in traceback.format_stack():

format_stack()从当前栈帧中提取原始回溯并将其格式化为元组列表。 我们使用for循环遍历元组列表。

$ ./stacktrace_ex.py
division by zero
Class: <class 'ZeroDivisionError'>
File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 30, in <module>
    test()
File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 27, in test
    myfun()
File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 23, in myfun
    myfun2()
File "C:/Users/Jano/PycharmProjects/Simple/simple.py", line 20, in myfun2
    for line in traceback.format_stack():

在程序中,我们可以看到调用栈-导致错误的被调用函数的顺序。

在本章中,我们介绍了 Python 中的异常。

Python 迭代器和生成器

原文: http://zetcode.com/lang/python/itergener/

在 Python 教程的这一部分中,我们将使用插入器和生成器。 迭代器是一个对象,它使程序员可以遍历集合的所有元素,而不管其具体实现如何。

在 Python 中,迭代器是实现迭代器协议的对象。 迭代器协议包含两种方法。 __iter__()方法必须返回迭代器对象,而next()方法必须返回序列中的下一个元素。

迭代器具有以下优点:

  • 干净的代码
  • 迭代器可以处理无限序列
  • 迭代器节省资源

Python 有几个内置对象,它们实现了迭代器协议。 例如列表,元组,字符串,字典或文件。

iterator.py

#!/usr/bin/env python

# iterator.py

str = "formidable"

for e in str:
   print(e, end=" ")

print()

it = iter(str)

print(it.next())
print(it.next())
print(it.next())

print(list(it))

在代码示例中,我们显示了字符串上的内置迭代器。 在 Python 中,字符串是不可变的字符序列。 iter()函数返回对象的迭代器。 我们还可以在迭代器上使用list()tuple()函数。

$ ./iterator.py 
f o r m i d a b l e
f
o
r
['m', 'i', 'd', 'a', 'b', 'l', 'e']

Python 阅读一行

通过节省系统资源,我们的意思是在使用迭代器时,我们可以获取序列中的下一个元素,而无需将整个数据集保留在内存中。

read_data.py

#!/usr/bin/env python

# read_data.py

with open('data.txt', 'r') as f:

    while True:

        line = f.readline()

        if not line: 
            break

        else: 
            print(line.rstrip())

此代码打印data.txt文件的内容。 除了使用while循环外,我们还可以应用迭代器来简化我们的任务。

read_data_iterator.py

#!/usr/bin/env python

# read_data_iterator.py

with open('data.txt', 'r') as f:

    for line in f:
        print(line.rstrip())

open()函数返回一个文件对象,它是一个迭代器。 我们可以在for循环中使用它。 通过使用迭代器,代码更清晰。

Python 迭代器协议

在下面的示例中,我们创建一个实现迭代器协议的自定义对象。

iterator_protocol.py

#!/usr/bin/env python

# iterator_protocol.py

class Seq:

   def __init__(self):

      self.x = 0

   def __next__(self):

      self.x += 1
      return self.x**self.x

   def __iter__(self):

      return self

s = Seq()
n = 0

for e in s:

   print(e)
   n += 1

   if n > 10:
      break

在代码示例中,我们创建了一个数字序列 1,4,27,256,...。 这表明使用迭代器,我们可以处理无限序列。

def __iter__(self):

    return self

for语句在容器对象上调用__iter__()函数。 该函数返回一个定义方法__next__()的迭代器对象,该方法一次访问一个容器中的元素。

 def next(self):
    self.x += 1
    return self.x**self.x

next()方法返回序列的下一个元素。

if n > 10:
    break

因为我们正在处理无限序列,所以我们必须中断for循环。

$ ./iterator.py 
1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611

StopIteration

循环可以以其他方式中断。 在类定义中,我们必须引发一个StopIteration异常。 在以下示例中,我们重做上一个示例。

stopiter.py

#!/usr/bin/env python

# stopiter.py

class Seq14:

   def __init__(self):
      self.x = 0

   def __next__(self):

      self.x += 1

      if self.x > 14:
         raise StopIteration

      return self.x ** self.x

   def __iter__(self):
      return self

s = Seq14()

for e in s:
   print(e)

该代码示例将打印序列的前 14 个数字。

if self.x > 14:
    raise StopIteration

StopIteration异常将停止for循环。

$ ./stop_iter.py 
1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611
8916100448256
302875106592253
11112006825558016

这是示例的输出。

Python 生成器

生成器是一个特殊的例程,可用于控制循环的迭代行为。 生成器类似于返回数组的函数。 生成器具有参数,可以调用它并生成数字序列。 但是,与返回整个数组的函数不同,生成器一次生成一个值。 这需要较少的内存。

Python 中的生成器:

  • def关键字定义
  • 使用yield关键字
  • 可以使用几个yield关键字
  • 返回一个迭代器

让我们看一个生成器示例。

simple_generator.py

#!/usr/bin/env python

# simple_generator.py

def gen():

   x, y = 1, 2
   yield x, y

   x += 1
   yield x, y

g = gen()

print(next(g))
print(next(g))

try:
   print(next(g))

except StopIteration:
   print("Iteration finished")

该程序将创建一个非常简单的生成器。

def gen():

   x, y = 1, 2
   yield x, y

   x += 1
   yield x, y

就像正常函数一样,生成器使用def关键字定义。 我们在生成器主体内使用两个yield关键字。 yield关键字退出生成器并返回一个值。 下次调用迭代器的next()函数时,我们在yield关键字之后的行继续。 请注意,局部变量会在整个迭代过程中保留。 当没有余量可产生时,将引发StopIteration异常。

$ ./generator.py 
(1, 2)
(2, 2)
Iteration finished

在以下示例中,我们计算斐波纳契数。 序列的第一个数字为 0,第二个数字为 1,并且每个后续数字等于序列本身前两个数字的和。

fibonacci_gen.py

#!/usr/bin/env python

# fibonacci_gen.py

import time

def fib():

   a, b = 0, 1

   while True:
      yield b

      a, b = b, a + b

g = fib()

try:
   for e in g:
      print(e)

      time.sleep(1)

except KeyboardInterrupt:
   print("Calculation stopped")

该脚本将 Fibonacci 数字连续打印到控制台。 用 Ctrl + C 组合键终止。

Python 生成器表达式

生成器表达式类似于列表推导。 区别在于生成器表达式返回的是生成器,而不是列表。

generator_expression.py

#!/usr/bin/env python

# generator_expression.py

n = (e for e in range(50000000) if not e % 3)

i = 0

for e in n:
    print(e)

    i += 1

    if i > 100:
        raise StopIteration

该示例计算的值可以除以 3,而没有余数。

n = (e for e in range(50000000) if not e % 3)

将使用圆括号创建生成器表达式。 在这种情况下,创建列表推导式将非常低效,因为该示例将不必要地占用大量内存。 为此,我们创建了一个生成器表达式,该表达式根据需要延迟生成值。

i = 0

for e in n:
    print(e)

    i += 1

    if i > 100:
        raise StopIteration

for循环中,我们使用生成器生成 100 个值。 我们这样做时并没有大量使用内存。

在下一个示例中,我们使用生成器表达式在 Python 中创建类似grep的工具。

roman_empire.txt

The Roman Empire (Latin: Imperium Rōmānum; Classical Latin: [ɪmˈpɛ.ri.ũː roːˈmaː.nũː] 
Koine and Medieval Greek: Βασιλεία τῶν Ῥωμαίων, tr. Basileia tōn Rhōmaiōn) was the 
post-Roman Republic period of the ancient Roman civilization, characterized by government 
headed by emperors and large territorial holdings around the Mediterranean Sea in Europe, 
Africa and Asia. The city of Rome was the largest city in the world c. 100 BC – c. AD 400, 
with Constantinople (New Rome) becoming the largest around AD 500,[5][6] and the Empire's 
populace grew to an estimated 50 to 90 million inhabitants (roughly 20% of the world's 
population at the time).[n 7][7] The 500-year-old republic which preceded it was severely 
destabilized in a series of civil wars and political conflict, during which Julius Caesar 
was appointed as perpetual dictator and then assassinated in 44 BC. Civil wars and executions 
continued, culminating in the victory of Octavian, Caesar's adopted son, over Mark Antony and 
Cleopatra at the Battle of Actium in 31 BC and the annexation of Egypt. Octavian's power was 
then unassailable and in 27 BC the Roman Senate formally granted him overarching power and 
the new title Augustus, effectively marking the end of the Roman Republic.

我们使用此文本文件。

generator_expression.py

#!/usr/bin/env python

# gen_grep.py

import sys

def grep(pattern, lines):
    return ((line, lines.index(line)+1) for line in lines if pattern in line)

file_name = sys.argv[2]
pattern = sys.argv[1]

with open(file_name, 'r') as f:
    lines = f.readlines()

    for line, n in grep(pattern, lines):
        print(n, line.rstrip())

该示例从文件中读取数据,并打印包含指定模式及其行号的行。

def grep(pattern, lines):
    return ((line, lines.index(line)+1) for line in lines if pattern in line)

类似于grep的工具使用此生成器表达式。 该表达式遍历行列表并选择包含模式的行。 它计算列表中该行的索引,即它在文件中的行号。

with open(file_name, 'r') as f:
    lines = f.readlines()

    for line, n in grep(pattern, lines):
        print(n, line.rstrip())

我们打开文件进行读取,然后对数据调用grep()函数。 该函数返回一个生成器,该生成器通过for循环遍历。

$ ./gen_grep.py Roman roman_empire.txt 
1 The Roman Empire (Latin: Imperium Rōmānum; Classical Latin: [ɪmˈpɛ.ri.ũː roːˈmaː.nũː]
3 post-Roman Republic period of the ancient Roman civilization, characterized by government
13 then unassailable and in 27 BC the Roman Senate formally granted him overarching power and
14 the new title Augustus, effectively marking the end of the Roman Republic.

文件中有四行包含Roman字。

在本章中,我们介绍了 Python 中的迭代器和生成器。

Python 内省

原文: http://zetcode.com/lang/python/introspection/

在 Python 教程的这一部分中,我们讨论了自省。

内省是一种自我检查的行为。 在计算机编程中,自省是在运行时确定对象的类型或属性的能力。 Python 编程语言对自省有很大的支持。 Python 中的所有内容都是一个对象。 Python 中的每个对象都可以具有属性和方法。 通过使用内省,我们可以动态检查 Python 对象。

Python dir函数

dir()函数返回属于对象的属性和方法的排序列表。

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

在这里,我们看到了元组对象的dir()函数的输出。

>>> print(().__doc__)
tuple() -> empty tuple
tuple(iterable) -> tuple initialized from iterable's items

If the argument is a tuple, the return value is the same object.

我们的研究表明,元组对象有一个__doc__属性。

direx.py

#!/usr/bin/env python

# direx.py

import sys

class MyObject(object):

   def __init__(self):
      pass

   def examine(self):
      print(self)

o = MyObject()

print(dir(o))
print(dir([]))
print(dir({}))
print(dir(1))
print(dir())
print(dir(len))
print(dir(sys))
print(dir("String"))

该示例使用dir()函数检查了几个对象:用户定义的对象,本机数据类型,函数,字符串或数字。

不带任何参数的dir()返回当前作用域中的名称。

>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> import sys
>>>import math, os
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'math', 'sys']

我们在包含一些模块之前和之后执行dir()函数。

Python type()函数

type()函数返回对象的类型。

typefun.py

#!/usr/bin/env python

# typefun.py

import sys

def function(): 
    pass

class MyObject(object):

   def __init__(self):
      pass

o = MyObject()

print(type(1))
print(type(""))
print(type([]))
print(type({}))
print(type(()))
print(type(object))
print(type(function))
print(type(MyObject))
print(type(o))
print(type(sys))

该示例将各种类型的对象打印到控制台屏幕。

$ ./typefun.py 
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
<class 'type'>
<class 'function'>
<class 'type'>
<class '__main__.MyObject'>
<class 'module'>

这是输出。

id()函数

id()返回对象的特殊 ID。

idfun.py

#!/usr/bin/env python

# idfun.py

import sys

def fun(): pass

class MyObject(object):

   def __init__(self):
      pass

o = MyObject()

print(id(1))
print(id(""))
print(id({}))
print(id([]))
print(id(sys))
print(id(fun))
print(id(MyObject))
print(id(o))
print(id(object))

该代码示例打印内置和自定义的各种对象的 ID。

$ ./idfun.py 
10914368
139696088742576
139696087935944
139696065155784
139696088325640
139696088244296
21503992
139696087910776
10738720

Python sys模块

sys模块提供对解释器使用或维护的系统特定变量和函数以及与解释器强烈交互的函数的访问。 该模块允许我们查询有关 Python 环境的信息。

>>> import sys
>>> sys.version
'3.5.2 (default, Nov 17 2016, 17:05:23) \n[GCC 5.4.0 20160609]'
>>> sys.platform
'linux'
>>> sys.path
['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', 
'/usr/lib/python3.5/lib-dynload', '/home/janbodnar/.local/lib/python3.5/site-packages', 
'/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

在上面的代码中,我们检查了 Python 版本,平台和搜索路径位置。

我们还可以使用dir()函数来获取sys模块的变量和函数的完整列表。

>>> sys.executable
'/usr/bin/python3'
>>> sys.argv
['']
>>> sys.byteorder
'little'

该示例显示了sys模块的executableargvbyteorder属性。

>>> sys.executable
'/usr/bin/python3'

可执行文件是一个字符串,给出有意义的系统上 Python 解释器的可执行二进制文件的名称。

>>> sys.argv
['']

这给出了传递给 Python 脚本的命令行参数列表。

>>> sys.byteorder
'little'

字节顺序是本机字节顺序的指示。 在大端(最高有效字节在前)平台上,值将为"big",在小端(最低有效字节在前)平台值将为"little"

其他自省

接下来,我们展示检查 Python 对象的各种其他方式。

attrs.py

#!/usr/bin/env python

# attr.py

def fun(): 
    pass

print(hasattr(object, '__doc__'))
print(hasattr(fun, '__doc__'))
print(hasattr(fun, '__call__'))

print(getattr(object, '__doc__'))
print(getattr(fun, '__doc__'))

hasattr()函数检查对象是否具有属性。 getattr()函数返回属性的内容(如果有的话)。

$ ./attr.py 
True
True
True
The most base type
None

isinstance函数检查对象是否为特定类的实例。

>>> print(isinstance.__doc__)
Return whether an object is an instance of a class or of a subclass thereof.

A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to
check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)
or ...`` etc.

我们可以交互地描述一个函数。

instance.py

#!/usr/bin/env python

# instance.py

class MyObject(object):

   def __init__(self):
      pass

o = MyObject()

print(isinstance(o, MyObject))
print(isinstance(o, object))
print(isinstance(2, int))
print(isinstance('str', str))

众所周知,一切都是 Python 中的对象; 偶数和字符串。 object是 Python 中所有对象的基本类型。

$ ./instance.py 
True
True
True
True

issubclass()函数检查特定的类是否是另一个类的派生类。

subclass.py

#!/usr/bin/env python

# subclass.py

class Object(object):

   def __init__(self):
      pass

class Wall(Object):

   def __init__(self):
      pass

print(issubclass(Object, Object))
print(issubclass(Object, Wall))
print(issubclass(Wall, Object))
print(issubclass(Wall, Wall))

在我们的代码示例中,Wall类是Object类的子类。 ObjectWall本身也是它们的子类。 Object类不是Wall类的子类。

$ ./subclass.py 
True
False
True
True

__doc__属性提供了有关对象的一些文档,__name__属性包含对象的名称。

namedoc.py

#!/usr/bin/env python

# namedoc.py

def noaction():
   '''A function, which does nothing'''
   pass

funcs = [noaction, len, str]

for i in funcs:

   print(i.__name__)
   print(i.__doc__)
   print("-" * 75)

在我们的示例中,我们创建了三个函数的列表:一个自定义函数和两个本机函数。 我们浏览列表并打印__name____doc__属性。

$ ./namedoc.py
noaction
A function, which does nothing
---------------------------------------------------------------------------
len
Return the number of items in a container.
---------------------------------------------------------------------------
str
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.
---------------------------------------------------------------------------

这是输出。

最后,还有一个callable()函数。 该函数检查对象是否为可调用对象(函数)。

callable.py

#!/usr/bin/env python

# callable.py

class Car(object):

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

def fun():
    pass

c = Car()    

print(callable(fun))
print(callable(c.setName))
print(callable([]))
print(callable(1))

在代码示例中,我们检查三个对象是否可调用。

print(callable(fun))
print(callable(c.setName))

fun()函数和setName()方法是可调用的。 (方法是绑定到对象的函数。)

$ ./callable.py
True
True
False
False

在 Python 教程的这一部分中,我们讨论了 Python 中的自省。 在inspect模块中可以找到更多进行内省的工具。

Python 教程

原文: http://zetcode.com/lang/python/

这是 Python 教程。 在本教程中,您将学习 Python 语言的基础知识和更高级的主题。

目录

Python

Python 是一种通用的,动态的,面向对象的编程语言。 Python 语言的设计目的强调程序员的生产力和代码可读性。

Python 相关教程

您可能对以下教程感兴趣: PyQt5 教程Pillow 教程Tkinter 教程OpenPyXL 教程wxPython 教程PySide 教程PyGTK 教程Python Requests 教程

{% raw %}

Python Faker 教程

原文: http://zetcode.com/python/faker/

Python Faker 教程展示了如何使用 Faker 包在 Python 中生成伪数据。 我们使用joke2k/faker库。

Faker

Faker 是一个生成伪造数据的 Python 库。 Faka 数据通常用于测试或用一些伪数据填充数据库。 Faker 受 PHP 的 Faker,Perl 的Data::Faker和 Ruby 的 Faker 的启发。

设置 Faker

该包随 composer 一起安装。

$ pip install Faker 

我们安装了 Faker 模块。

$ pip install Dumper

另外,我们安装了 Dumper,它在转储变量时提供更好的控制台输出。

Faker 生成器

faker.Faker()创建并初始化一个伪造的生成器,该伪造器可以通过访问以数据类型命名的属性来生成数据。

Faker 将数据生成委托给提供者。 默认供应器使用英语语言环境。 Faker 支持其他语言环境; 他们的完成水平不同。

简单的 Faker

以下示例是 Faker 的简单演示。

simple.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'name: {faker.name()}')
print(f'address: {faker.address()}')

print(f'text: {faker.text()}')

该示例输出假名称,地址和文本。

$ ./simple.py
name: Arthur Patton
address: 0638 Larsen Way
Tylermouth, CA 48344
text: Save general start bar. Become class both bank Mrs so myself.
Each difficult performance even. Total anyone develop her raise research both.
Laugh sport necessary tree. As during day up fall.

这是一个示例输出。

Faker 名称

在第二个示例中,我们伪造与用户名有关的数据。

names.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Name: {faker.name()}')
print(f'First name: {faker.first_name()}')
print(f'Last name: {faker.last_name()}')

print('--------------------------')

print(f'Male name: {faker.name_male()}')
print(f'Female name: {faker.name_female()}')

该示例创建假的全名,男性的姓氏和姓氏。

$ ./names.py
Name: Tara Brown
First name: Stephanie
Last name: Martin
--------------------------
Male name: John Banks
Female name: Lacey Roberts

这是一个示例输出。

Faker 工作

作业由job()生成。

jobs.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

for _ in range(6):
    print(faker.job())

该示例创建了六个伪造作业。

$ ./jobs.py
Town planner
Paediatric nurse
Geographical information systems officer
Water quality scientist
Engineer, maintenance
Designer, ceramics/pottery

这是一个示例输出。

Faker 语言环境数据

Faker 在某种程度上支持本地化数据。 语言环境被传递给构造方法。 请注意,语言环境已完成各种级别。

localized.py

#!/usr/bin/env python

from faker import Faker

faker = Faker('cz_CZ')

for i in range(3):

    name = faker.name()
    address = faker.address()
    phone = faker.phone_number()

    print(f'{name}, {address}, {phone}')

该示例使用捷克语生成伪造数据。

$ ./localized.py
Irena Novotná, Nad Šancemi 725
055 80 Bojkovice, 606 136 053
Stanislav Svoboda, Březanova 225
621 17 Bystřice pod Hostýnem, 727 057 251
Klára Moravcová, Neužilova 6/3
134 97 Česká Kamenice, 606 374 469

这是一个示例输出。 请注意,捷克语具有口音。

Faker 货币

以下示例为货币创建伪造数据。

currencies.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'currency: {faker.currency()}')
print(f'currency name: {faker.currency_name()}')

print(f'currency code: {faker.currency_code()}')

该程序生成假货币。

$ ./currencies.py
currency: ('ISK', 'Icelandic króna')
currency name: Israeli new shekel
currency code: DJF

这是一个示例输出。

假话

Faker 允许创建假单词。

words.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'a word: {faker.word()}')
print(f'six words: {faker.words(6)}')

words = ['forest', 'blue', 'cloud', 'sky', 'wood', 'falcon']

print(f'customized unique words: {faker.words(3, words, True)}')

该示例创建伪单词。

print(f'a word: {faker.word()}')

该行生成一个伪造的单词。

print(f'six words: {faker.words(6)}')

在这里,我们生成六个伪单词。

words = ['forest', 'blue', 'cloud', 'sky', 'wood', 'falcon']

print(f'customized unique words: {faker.words(3, words, True)}')

我们还可以从预定义的单词列表中创建假单词。

$ words.py
a word: show
six words: ['value', 'its', 'those', 'wish', 'enter', 'hold']
customized unique words: ['forest', 'blue', 'sky']

这是一个示例输出。

Faker 个人数据

Faker 可以使用simple_profile()创建简单的虚拟配置文件,并使用profile()创建扩展的配置文件。

profiles.py

#!/usr/bin/env python

from faker import Faker
import dumper

faker = Faker()

profile1 = faker.simple_profile()
dumper.dump(profile1)

print('--------------------------')

profile2 = faker.simple_profile('M')
dumper.dump(profile2)

print('--------------------------')

profile3 = faker.profile(sex='F')
dumper.dump(profile3)

该示例为男性和女性创建虚拟概要文件。

$ ./profiles.py
<dict at 0x20d590a7678>:
  username: 'gmorgan'
  name: 'Jessica Clark'
  sex: 'F'
  address: '694 Joseph Valleys\nJohnfort, ME 81780'
  mail: 'bettybuckley@yahoo.com'
  birthdate: <str at 0x20d5bcbd7b0>: 'datetime.date(1938, 9, 18)'
--------------------------
<dict at 0x20d5b0065e8>:
  username: 'mrios'
  name: 'Harold Wagner'
  sex: 'M'
  address: '26439 Robinson Radial\nWest Meghanmouth, PA 85463'
  mail: 'josechoi@gmail.com'
  birthdate: <str at 0x20d5bcbd7b0>: 'datetime.date(1986, 8, 18)'
--------------------------
<dict at 0x20d5bd24990>:
  job: 'Engineer, communications'
  company: 'Jackson-Willis'
  ssn: '430-41-6118'
  residence: 'USNS Odom\nFPO AP 47095'
  current_location: <tuple at 0x20d5bca9a88>
    0: <str at 0x20d5bd248a0>: "Decimal('74.1885785')"
    1: <str at 0x20d5bd248a0>: "Decimal('119.951995')"
  blood_group: 'O-'
  website: ['http://roberson.com/']
  username: 'ygutierrez'
  name: 'Lindsay Walker'
  sex: 'F'
  address: '22968 Beverly Road Suite 918\nTimothyborough, SD 10494'
  mail: 'aliciamccall@yahoo.com'
  birthdate: <str at 0x20d5bcbd7b0>: 'datetime.date(1979, 1, 4)'

这是一个示例输出。

Faker 号码

Faker 允许生成随机数字和整数。

fake_numbers.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Random int: {faker.random_int()}')
print(f'Random int: {faker.random_int(0, 100)}')
print(f'Random digit: {faker.random_digit()}')

该示例生成随机数字和整数。

print(f'Random int: {faker.random_int(0, 100)}')

我们可以在random_int()方法中指定界限。

$ ./fake_numbers.py
Random int: 5181
Random int: 91
Random digit: 9

这是一个示例输出。

Faker 哈希和 uuid

虚假哈希和 uuid 的 Faker 支持。

hash_uuid.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'md5: {faker.md5()}')
print(f'sha1: {faker.sha1()}')
print(f'sha256: {faker.sha256()}')
print(f'uuid4: {faker.uuid4()}')

该示例生成三个假哈希和一个 uuid 值。

$ ./hash_uuid.py
md5: aace57d56794686acec9eb190d401d46
sha1: 9f8f6af3089e7b5ed571591701afcfd9c2bb7a0e
sha256: 8b117b58599809f50735c701f598312b0f64203a8ffacde09af23db69cfd62d5
uuid4: 38092dcd-0e49-4ac0-b39b-7edf6db62290

这是一个示例输出。

Faker 互联网相关数据

Faker 有多个用于伪造与互联网相关的数据的访问器。

internet.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Email: {faker.email()}')
print(f'Safe email: {faker.safe_email()}')
print(f'Free email: {faker.free_email()}')
print(f'Company email: {faker.company_email()}')

print('------------------------------------')

print(f'Host name: {faker.hostname()}')
print(f'Domain name: {faker.domain_name()}')
print(f'Domain word: {faker.domain_word()}')
print(f'TLD: {faker.tld()}')

print('------------------------------------')

print(f'IPv4: {faker.ipv4()}')
print(f'IPv6: {faker.ipv6()}')
print(f'MAC address: {faker.mac_address()}')

print('------------------------------------')

print(f'Slug: {faker.slug()}')
print(f'Image URL: {faker.image_url()}')

该示例显示了各种与互联网相关的数据,包括电子邮件,域名,信息,IP 地址和 URL。

$ ./internet.py
Email: hescobar@acevedo.info
Safe email: jonesgregory@example.net
Free email: zchambers@yahoo.com
Company email: paulbailey@gordon-woods.com
------------------------------------
Host name: desktop-12.rodriguez-underwood.com
Domain name: henry.com
Domain word: davis
TLD: com
------------------------------------
IPv4: 192.31.48.26
IPv6: 75cd:2c43:37f5:774c:dd:5a2f:ae5d:bfc9
MAC address: 3d:b1:39:ec:c6:53
------------------------------------
Slug: of-street-fight
Image URL: https://placeimg.com/311/871/any

这是一个示例输出。

Faker 日期和时间

Faker 有很多伪造日期和时间值的方法。

date_time.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Date of birth: {faker.date_of_birth()}')
print(f'Century: {faker.century()}')
print(f'Year: {faker.year()}')
print(f'Month: {faker.month()}')
print(f'Month name: {faker.month_name()}')
print(f'Day of week: {faker.day_of_week()}')
print(f'Day of month: {faker.day_of_month()}')
print(f'Time zone: {faker.timezone()}')
print(f'AM/PM: {faker.am_pm()}')

第一个示例显示了伪造的生日,日期时间部分,时区和 AM/PM 方法。

$ date_time.py
Date of birth: 2008-08-07
Century: IV
Year: 1981
Month: 05
Month name: January
Day of week: Saturday
Day of month: 26
Time zone: Asia/Oral
AM/PM: AM

这是一个示例输出。

datetime2.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Datetime this century: {faker.date_time_this_century()}')
print(f'Datetime this decade: {faker.date_time_this_decade()}')
print(f'Datetime this year: {faker.date_time_this_year()}')
print(f'Datetime this month: {faker.date_time_this_month()}')

print('-------------------------')

print(f'Date this century: {faker.date_this_century()}')
print(f'Date this decade: {faker.date_this_decade()}')
print(f'Date this year: {faker.date_this_year()}')
print(f'Date this month: {faker.date_this_month()}')

print('-------------------------')

TOTAL_SECONDS = 60*60*24*2 # two days

series = faker.time_series(start_date='-12d', end_date='now', precision=TOTAL_SECONDS)

for val in series:
    print(val[0])

第二个示例显示了用于生成当前世纪,十年,年份或月份中的日期时间值的方法。 它还包括时间序列值的生成。

$ date_time2.py
Datetime this century: 2013-08-06 23:42:47
Datetime this decade: 2010-02-15 01:08:34
Datetime this year: 2019-03-10 05:32:42
Datetime this month: 2019-04-06 10:13:53
-------------------------
Date this century: 2014-11-04
Date this decade: 2018-03-07
Date this year: 2019-02-07
Date this month: 2019-04-04
-------------------------
2019-03-26 16:16:49
2019-03-28 16:16:49
2019-03-30 16:16:49
2019-04-01 16:16:49
2019-04-03 16:16:49
2019-04-05 16:16:49

这是一个示例输出。

datetime3.py

#!/usr/bin/env python

from faker import Faker

faker = Faker()

print(f'Unix time: {faker.unix_time()}')
print(f'Datetime: {faker.date_time()}')
print(f'iso8601: {faker.iso8601()}')
print(f'Date: {faker.date()}')
print(f'Time: {faker.time()}')

print('-------------------------')

print(f"Datetime between: {faker.date_time_between(start_date='-15y', end_date='now')}")
print(f"Date between: {faker.date_between()}")

print('-------------------------')

print(f"Future datetime: {faker.future_datetime()}")
print(f"Future date: {faker.future_date()}")
print(f"Past datetime: {faker.past_datetime()}")
print(f"Past date: {faker.past_date()}")

第三个示例显示了用于各种日期时间格式,获取选定范围的日期时间值以及生成未来或过去值的方法。

$ date_time3.py
Unix time: 1389138339
Datetime: 1988-01-24 09:16:09
iso8601: 2014-04-22T04:19:18
Date: 2009-05-01
Time: 12:07:51
-------------------------
Datetime between: 2011-03-18 21:00:23
Date between: 1992-07-09
-------------------------
Future datetime: 2019-04-11 13:36:02
Future date: 2019-04-14
Past datetime: 2019-03-21 12:27:20
Past date: 2019-03-22

这是一个示例输出。

用 Faker 生成 XML 数据

在以下示例中,我们使用 Faker 和 Jinja2 模板生成 XML 数据。 XML 文件将包含用户。

$ pip install jinja2

我们安装 Jinja2 模板引擎。

fake_xml.py

#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader
from faker import Faker

class User:

    def __init__(self, first_name, last_name, occupation):

        self.first_name = first_name
        self.last_name = last_name
        self.occupation = occupation

faker = Faker()
users = []

for _ in range(10):

    first_name = faker.first_name()
    last_name = faker.last_name()
    occupation = faker.job()

    user = User(first_name, last_name, occupation)

    users.append(user)

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('users.xml.j2')
output = template.render(users=users)

print(output, file=open('users.xml', 'w'))

该程序将生成十个用户的列表。 该列表将传递到 Jinja2 模板进行处理。 模板位于templates目录中。 生成的内容被写入users.xml文件。

templates/users.xml.j2

<?xml version="1.0" encoding="UTF-8"?>
<users>
    {% for user in users %}
    <user id="{{ loop.index }}">
        <firstname>{{ user.first_name }}</firstname>
        <lastname>{{ user.last_name }}</lastname>
        <occupation>{{ user.occupation }}</occupation>
    </user>
    {% endfor %}
</users>

在模板中,我们使用for指令来处理用户列表。

在本教程中,我们使用 Python Faker 在 Python 中生成伪数据。

您可能也对以下相关教程感兴趣: Python Jinja 教程Python 教程,或列出所有 Python 教程

{% endraw %}

{% raw %}

Python f 字符串教程

原文: http://zetcode.com/python/fstring/

Python f 字符串教程显示了如何使用 f 字符串在 Python 中格式化字符串。

Python f 字符串

Python f 字符串是执行字符串格式化的最新 Python 语法。 自 Python 3.6 起可用。 Python f 字符串提供了一种更快,更易读,更简明且不易出错的在 Python 中格式化字符串的方式。

f 字符串的前缀为f,并使用{}括号求值值。

在冒号后指定用于类型,填充或对齐的格式说明符; 例如:f'{price:.3}',其中price是变量名。

Python 字符串格式

以下示例总结了 Python 中的字符串格式设置选项。

formatting_strings.py

#!/usr/bin/env python3

name = 'Peter'
age = 23

print('%s is %d years old' % (name, age))
print('{} is {} years old'.format(name, age))
print(f'{name} is {age} years old')

该示例使用两个变量设置字符串格式。

print('%s is %d years old' % (name, age))

这是最旧的选项。 它使用%运算符和经典字符串格式指定,例如%s%d

print('{} is {} years old'.format(name, age))

从 Python 3.0 开始,format()函数被引入以提供高级格式化选项。

print(f'{name} is {age} years old')

从 Python 3.6 开始,Python f 字符串可用。 该字符串具有f前缀,并使用{}求值变量。

$ python formatting_string.py
Peter is 23 years old
Peter is 23 years old
Peter is 23 years old

我们有相同的输出。

Python f 字符串表达式

我们可以将表达式放在{}括号之间。

expressions.py

#!/usr/bin/env python3

bags = 3
apples_in_bag = 12

print(f'There are total of {bags * apples_in_bag} apples')

该示例对 f 字符串中的表达式求值。

$ python expressions.py
There are total of 36 apples

这是输出。

Python f 字符串字典

我们可以使用 f 字符串中的字典。

dicts.py

#!/usr/bin/env python3

user = {'name': 'John Doe', 'occupation': 'gardener'}

print(f"{user['name']} is a {user['occupation']}")

该示例以 f 字符串形式求值字典。

$ python dicts.py
John Doe is a gardener

这是输出。

Python 多行 f 字符串

我们可以使用多行字符串。

multiline.py

#!/usr/bin/env python3

name = 'John Doe'
age = 32
occupation = 'gardener'

msg = (
    f'Name: {name}\n'
    f'Age: {age}\n'
    f'Occupation: {occupation}'
)

print(msg)

该示例显示了多行 f 字符串。 F 字符串放在方括号之间; 每个字符串前面都带有f字符。

$ python multiline.py
Name: John Doe
Age: 32
Occupation: gardener

这是输出。

Python f 字符串调用函数

我们还可以在 f 字符串中调用函数。

call_function.py

#!/usr/bin/env python3

def mymax(x, y):

    return x if x > y else y

a = 3
b = 4

print(f'Max of {a} and {b} is {mymax(a, b)}')

该示例在 f 字符串中调用自定义函数。

$ python call_fun.py
Max of 3 and 4 is 4

这是输出。

Python f 字符串对象

Python f 字符串也接受对象。 对象必须定义了__str__()__repr__()魔术函数。

objects.py

#!/usr/bin/env python3

class User:
    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation

    def __repr__(self):
        return f"{self.name} is a {self.occupation}"

u = User('John Doe', 'gardener')

print(f'{u}')

该示例求值 f 字符串中的对象。

$ python objects.py
John Doe is a gardener

这是输出。

Python F 字符串转义字符

下面的示例显示如何对 f 字符串中的某些字符进行转义。

escaping.py

#!/usr/bin/env python3

print(f'Python uses {{}} to evaludate variables in f-strings')
print(f'This was a \'great\' film')

为了避免花括号,我们将字符加倍。 单引号以反斜杠字符转义。

$ python escaping.py
Python uses {} to evaludate variables in f-strings
This was a 'great' film

这是输出。

Python f 字符串格式化日期时间

以下示例格式化日期时间。

format_datetime.py

#!/usr/bin/env python3

import datetime

now = datetime.datetime.now()

print(f'{now:%Y-%m-%d %H:%M}')

该示例显示格式化的当前日期时间。 日期时间格式说明符位于:字符之后。

$ python format_datetime.py
2019-05-11 22:39

这是输出。

Python f 字符串格式化浮点数

浮点值的后缀为f。 我们还可以指定精度:小数位数。 精度是一个点字符后的值。

format_floats.py

#!/usr/bin/env python3

val = 12.3

print(f'{val:.2f}')
print(f'{val:.5f}')

该示例打印格式化的浮点值。

$ python format_floats.py
12.30
12.30000

输出显示具有两位和五个小数位的数字。

Python f 字符串格式化宽度

宽度说明符设置值的宽度。 如果该值短于指定的宽度,则该值可以用空格或其他字符填充。

format_width.py

#!/usr/bin/env python3

for x in range(1, 11):
    print(f'{x:02} {x*x:3} {x*x*x:4}')

该示例打印三列。 每个列都有一个预定义的宽度。 第一列使用 0 填充较短的值。

$ python format_width.py
01   1    1
02   4    8
03   9   27
04  16   64
05  25  125
06  36  216
07  49  343
08  64  512
09  81  729
10 100 1000

这是输出。

Python f 字符串对齐字符串

默认情况下,字符串在左边对齐。 我们可以使用>字符来对齐右侧的字符串。 >字符在冒号后面。

justify.py

#!/usr/bin/env python3

s1 = 'a'
s2 = 'ab'
s3 = 'abc'
s4 = 'abcd'

print(f'{s1:>10}')
print(f'{s2:>10}')
print(f'{s3:>10}')
print(f'{s4:>10}')

我们有四个不同长度的弦。 我们将输出的宽度设置为十个字符。 值在右对齐。

$ python justify.py
         a
        ab
       abc
      abcd

这是输出。

Python f 字符串数字符号

数字可以具有各种数字符号,例如十进制或十六进制。

format_notations.py

#!/usr/bin/env python3

a = 300

# hexadecimal
print(f"{a:x}")

# octal
print(f"{a:o}")

# scientific
print(f"{a:e}")

该示例以三种不同的表示法打印值。

$ python format_notations.py
12c
454
3.000000e+02

这是输出。

在本教程中,我们使用了 Python f 字符串。

您可能也对以下相关教程感兴趣: Python 字符串Python Jinja 教程Python 教程,或列出所有 Python 教程

{% endraw %}

Python bcrypt教程

原文: http://zetcode.com/python/bcrypt/

Python bcrypt教程展示了如何使用bcrypt库对 Python 中的密码进行哈希处理。 它定义了基本术语,包括加密,哈希和盐。

Python bcrypt模块是一个用于在 Python 中生成强哈希值的库。 它通过pip install bcrypt命令安装。

加密

加密是对消息或信息进行编码的过程,以使只有授权人员才能使用相应的键读取消息或信息,而未经授权的人员则不能。 预期的信息或消息,称为纯文本,使用加密算法-密码-加密,生成密文,只有解密后才能读取。 加密是一种双向函数。 当我们加密某些东西时,我们这样做是为了以后对其进行解密。 加密用于传输时保护数据; 例如在邮件通讯中。

哈希

哈希是使用算法将任意大小的数据映射到固定长度的过程。 这称为哈希值。 加密是一种双向功能,而哈希是一种单向函数。 尽管在技术上可以反向哈希值,但所需的计算能力使其不可行。 加密是为了保护传输中的数据,而哈希是为了验证数据没有被更改并且是真实的。

注意:哈希不限于安全性。 它还用于比较大量数据或快速键查找。

密码不是以纯文本格式存储在数据库中,而是以哈希值存储。

盐是固定长度的加密强度强的随机值,将其添加到哈希函数的输入中以为每个输入创建唯一的哈希。 添加盐可以使密码哈希输出唯一,即使对于采用通用密码的用户也是如此。

bcrypt哈希函数

bcrypt是 Niels Provos 和 DavidMazières 基于 Blowfish 密码设计的密码哈希函数。 bcrypt函数是 OpenBSD 的默认密码哈希算法。 有针对 C,C++ ,C# ,Java,JavaScript,PHP,Python 和其他语言的bcrypt实现。

bcrypt算法使用强大的加密技术为我们创建哈希并加盐。 该算法的计算成本是参数化的,因此随着计算机变得越来越快,它可能会增加。 计算成本被称为工作因子或成本因子。 它减慢了哈希的速度,使暴力破解的尝试越来越难。 随着计算机变得越来越快,最佳成本因数会随着时间而变化。 高成本因素的缺点是增加了系统资源的负载并影响了用户体验。

Python bcrypt创建哈希密码

在下一个示例中,我们创建一个哈希密码。

create_hashed_password.py

#!/usr/bin/env python3

import bcrypt

passwd = b's$cret12'

salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(passwd, salt)

print(salt)
print(hashed)

该示例使用bcrypt创建一个盐和一个哈希密码。

import bcrypt

我们导入bcrypt模块。

salt = bcrypt.gensalt()

gensalt()函数生成盐。

hashed = bcrypt.hashpw(passwd, salt)

使用hashpw()函数创建一个哈希值,该函数将明文值和盐作为参数。

$ python first.py
b'$2b$12$mwSIOyxLJid1jFLgnU0s0.'
b'$2b$12$mwSIOyxLJid1jFLgnU0s0.7pmzp8Mtx.GEO30x0AbI2v8r2sb98Cy'
$ python first.py
b'$2b$12$MgGs11HIXGkg1Bm1Epw0Du'
b'$2b$12$MgGs11HIXGkg1Bm1Epw0Du20TV8ppi2Latgq7kKng8UjM5ZFWKKeS'

请注意,盐是生成的哈希值的第一部分。 还要注意,每次生成唯一的盐和哈希值。

Python bcrypt检查密码

下面的示例根据哈希值检查密码。

check_passwd.py

#!/usr/bin/env python3

import bcrypt

passwd = b's$cret12'

salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(passwd, salt)

if bcrypt.checkpw(passwd, hashed):
    print("match")
else:
    print("does not match")

使用checkpw()函数检查密码。

$ python check_passwd.py
match

这是输出。

Python bcrypt成本因子

成本因子通过减慢哈希来提高安全性。

cost_factor.py

#!/usr/bin/env python3

import bcrypt
import time

passwd = b's$cret12'

start = time.time()
salt = bcrypt.gensalt(rounds=16)
hashed = bcrypt.hashpw(passwd, salt)
end = time.time()

print(end - start)

print(hashed)

我们使用rounds参数将成本因子设置为 16。 我们测量生成密码哈希的时间。

$ cost_factor.py
4.268407821655273
b'$2b$16$.1FczuSNl2iXHmLojhwBZO9vCfA5HIqrONkefhvn2qLQpth3r7Jwe'

花费超过四秒钟的时间来生成具有指定成本因子的哈希值。

在本教程中,我们使用了 Python bcrypt模块来生成密码哈希。

您可能也对以下相关教程感兴趣: Python 教程Python 列表推导所有 Python 教程

Python 套接字教程

原文: http://zetcode.com/python/socket/

Python 套接字教程展示了如何使用套接字进行 Python 网络编程。 套接字编程是低级的。 本教程的目的是介绍包括这些低级详细信息的网络编程。 有更高级的 Python API,例如 Twisted,可能更适合。

在编程中,套接字是网络上运行的两个程序之间通信的端点。 套接字用于在客户端程序和服务器程序之间创建连接。

Python 的socket模块提供了与 Berkeley 套接字 API 的接口。

注意:在网络中,“套接字”一词具有不同的含义。 它用于 IP 地址和端口号的组合。

网络协议

TCP/IP 是设备用于在互联网和大多数本地网络上进行通信的一组协议。 TCP 更可靠,具有大量错误检查并需要更多资源。 HTTP,SMTP 或 FTP 等服务使用它。 UDP 的可靠性要差得多,错误检查的能力也有限,所需资源也更少。 VoIP 等服务使用它。

socket.SOCK_STREAM用于为 TCP 创建套接字,而socket.SOCK_DGRAM为 UDP 创建套接字。

地址族

创建套接字时,必须指定其地址族。 然后,我们只能在套接字中使用该类型的地址。

  • AF_UNIXAF_LOCAL - 本地通讯
  • AF_INET - IPv4 互联网协议
  • AF_INET6 - IPv6 互联网协议
  • AF_IPX - IPX-Novell 协议
  • AF_BLUETOOTH - 无线蓝牙协议
  • AF_PACKET - 底层数据包接口

对于AF_INET地址族,指定了一对(主机,端口)。 host是一个字符串,表示互联网域表示法中的主机名(如example.com)或 IPv4 地址(如93.184.216.34),并且port是整数。

Python 获取 IP 地址

使用gethostbyname(),我们获得主机的 IP 地址。

get_ip.py

#!/usr/bin/env python

import socket

ip = socket.gethostbyname('example.com')
print(ip)

该示例打印example.com的 IP 地址。

$ ./get_ip.py
93.184.216.34

这是输出。

Python UDP 套接字示例

UDP 是一种通信协议,它通过网络传输独立的数据包,不保证到达且也不保证传递顺序。 使用 UDP 的一项服务是每日报价(QOTD)。

qotd_client.py

#!/usr/bin/env python

import socket

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:

    message = b''
    addr = ("djxmmx.net", 17)

    s.sendto(message, addr)

    data, address = s.recvfrom(1024)
    print(data.decode())

该示例创建一个连接到 QOTD 服务的客户端程序。

import socket

我们导入socket模块。

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:

创建了用于 IPv4 的数据报套接字。

message = b''

我们发送空消息; QOTD 服务通过向套接字发送任意数据来工作; 它只是用引号引起来。 为了通过 TCP/UDP 进行通信,我们使用二进制字符串。

addr = ("djxmmx.net", 17)

我们提供地址和端口。

s.sendto(message, addr)

我们使用sendto()方法发送数据。

data, address = s.recvfrom(1024)

UDP 套接字使用recvfrom()接收数据。 它的参数是缓冲区大小。 返回值是一对(数据,地址),其中数据是代表接收到的数据的字节字符串,而地址是发送数据的套接字的地址。

print(data.decode())

我们将解码后的数据打印到终端。

$ ./qotd_client.py
"Oh the nerves, the nerves; the mysteries of this machine called man!
    Oh the little that unhinges it, poor creatures that we are!"
    Charles Dickens (1812-70)

这是一个示例输出。

Python TCP 套接字示例

是提供当前时间的服务器。 客户端无需任何命令即可直接连接到服务器,服务器以当前时间作为响应。

注意:时间服务器来来往往,因此我们可能需要在 https://www.ntppool.org/en/ 上找到可用的服务器。

time_client.py

#!/usr/bin/env python

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

    host = "time.nist.gov"
    port = 13

    s.connect((host, port))
    s.sendall(b'')
    print(str(s.recv(4096), 'utf-8'))

该示例通过连接到时间服务器的 TCP 套接字来确定当前时间。

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

创建用于 IPv4 的 TCP 套接字。

host = "time.nist.gov"
port = 13

这是工作时间服务器的主机名和端口号。

s.connect((host, port))

我们使用connect()连接到远程套接字。

s.sendall(b'')

sendall()方法将数据发送到套接字。 套接字必须连接到远程套接字。 它继续从字节发送数据,直到发送完所有数据或发生错误为止。

print(str(s.recv(4096), 'utf-8'))

我们打印接收到的数据。 recv()方法从套接字接收最多buffersize个字节。 当没有数据可用时,它将阻塞,直到至少一个字节可用或直到远端关闭为止。 当远端关闭并读取所有数据时,它将返回一个空字节字符串。

Python 套接字 HEAD 请求

HEAD 请求是没有消息正文的 GET 请求。 请求/响应的标头包含元数据,例如 HTTP 协议版本或内容类型。

head_request.py

#!/usr/bin/env python

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

    s.connect(("webcode.me" , 80))
    s.sendall(b"HEAD / HTTP/1.1\r\nHost: webcode.me\r\nAccept: text/html\r\n\r\n")
    print(str(s.recv(1024), 'utf-8'))

在示例中,我们向webcode.me发送 HEAD 请求。

s.sendall(b"HEAD / HTTP/1.1\r\nHost: webcode.me\r\nAccept: text/html\r\n\r\n")

HEAD命令发出头请求,后跟资源 URL 和 HTTP 协议版本。 请注意,\r\n是通信过程中必不可少的部分。 在 RFC 7231 文档中描述了详细信息。

$ head_request.py
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 08 Sep 2019 11:23:25 GMT
Content-Type: text/html
Content-Length: 348
Last-Modified: Sat, 20 Jul 2019 11:49:25 GMT
Connection: keep-alive
ETag: "5d32ffc5-15c"
Accept-Ranges: bytes

这是输出。

Python 套接字 GET 请求

HTTP GET 方法请求指定资源的表示形式。 使用 GET 的请求应仅检索数据。

get_request.py

#!/usr/bin/env python

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

    s.connect(("webcode.me" , 80))
    s.sendall(b"GET / HTTP/1.1\r\nHost: webcode.me\r\nAccept: text/html\r\nConnection: close\r\n\r\n")

    while True:

        data = s.recv(1024)

        if not data:
            break

        print(data.decode())

该示例使用 GET 请求读取webcode.me的主页。

s.sendall(b"GET / HTTP/1.1\r\nHost: webcode.me\r\nAccept: text/html\r\nConnection: close\r\n\r\n")

对于 HTTP 1.1 协议,默认情况下,连接可以是持久的。 这就是为什么我们发送Connection: close标头的原因。

while True:

    data = s.recv(1024)

    if not data:
        break

    print(data.decode())

我们使用while循环来处理接收到的数据。 如果没有错误发生,则recv()返回接收到的字节。 如果连接已正常关闭,则返回值为空字节字符串。 recv()是一种阻止方法,直到完成它,或者达到超时或发生另一个异常为止。

$ ./get_request.py
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Sun, 08 Sep 2019 11:39:34 GMT
Content-Type: text/html
Content-Length: 348
Last-Modified: Sat, 20 Jul 2019 11:49:25 GMT
Connection: keep-alive
ETag: "5d32ffc5-15c"
Access-Control-Allow-Origin: *
Accept-Ranges: bytes

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My html page</title>
</head>
<body>

    <p>
        Today is a beautiful day. We go swimming and fishing.
    </p>

    <p>
         Hello there. How are you?
    </p>

</body>
</html>

这是输出。

回显客户端服务器示例

回显服务器将来自客户端的消息发送回去。 这是用于测试和学习的经典示例。

echo_server.py

#!/usr/bin/env python

import socket
import time

with socket.socket() as s:

    host = 'localhost'
    port = 8001

    s.bind((host, port))
    print(f'socket binded to {port}')

    s.listen()

    con, addr = s.accept()

    with con:

        while True:

            data = con.recv(1024)

            if not data:
                break

            con.sendall(data)

回显服务器将客户端消息发送回客户端。

host = 'localhost'
port = 8001

服务器在端口 8001 上的 localhost 上运行。

s.bind((host, port))

bind()方法建立通信端点。 它将套接字绑定到指定的地址。 套接字必须尚未绑定。 (地址的格式取决于地址系列。)

s.listen()

listen()方法使服务器可以接受连接。 服务器现在可以监听套接字上的连接。 listen()具有backlog参数。 它指定系统在拒绝新连接之前允许的不可接受的连接数。 自 Python 3.5 起,此参数是可选的。 如果未指定,则选择默认的积压值。

con, addr = s.accept()

使用accept(),服务器接受连接。 它阻止并等待传入​​连接。 套接字必须绑定到一个地址并监听连接。 返回值是一对(con, addr),其中con是可用于在连接上发送和接收数据的新套接字对象,而addr是绑定到连接另一端上的套接字的地址。

请注意,accept()创建了一个新的套接字,用于与客户端进行通信,该套接字与监听套接字不同。

echo_client.py

#!/usr/bin/env python

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

    host = "localhost"
    port = 8001

    s.connect((host, port))
    s.sendall(b'hello there')
    print(str(s.recv(4096), 'utf-8'))

客户端将消息发送到回显服务器。

异步服务器示例

为了提高服务器的性能,我们可以使用asyncio模块。

async_server.py

#!/usr/bin/env python

# from threading import current_thread

import asyncio

async def handle_client(reader, writer):

    data = (await reader.read(1024))

    writer.write(data)
    writer.close()

loop = asyncio.get_event_loop()
loop.create_task(asyncio.start_server(handle_client, 'localhost', 8001))
loop.run_forever()

现在,我们可以测试阻塞服务器和非阻塞服务器的性能。

$ ab -c 50 -n 1000 http://localhost:8001/

例如,我们可以使用 Apache 基准测试工具测试性能。 在我们的例子中,命令发送 1000 个请求,一次发送 50 个。

在本教程中,我们展示了如何在 Python 中使用套接字创建简单的网络程序。

您可能也对以下相关教程感兴趣: Python 字符串Python Jinja 教程Python 教程,或列出所有 Python 教程

Python smtplib教程

原文: http://zetcode.com/python/smtplib/

Python smtplib教程展示了如何使用smtplib模块在 Python 中发送电子邮件。 要发送电子邮件,我们使用 Python 开发服务器,Mailtrap 在线服务和共享的网络托管邮件服务器。

SMTP

简单邮件传输协议(SMTP)是用于电子邮件传输的通信协议。它是一个互联网标准,该标准于 1982 年由 RFC 821 首次定义,并于 2008 年由 RFC 5321 更新为扩展 SMTP 添加。 邮件服务器和其他邮件传输代理使用 SMTP 发送和接收邮件。

smtplib模块

smtplib是一个 Python 库,用于使用简单邮件传输协议(SMTP)发送电子邮件。 smtplib是内置模块; 我们不需要安装它。 它抽象了 SMTP 的所有复杂性。

邮件服务器

要实际发送电子邮件,我们需要有权访问邮件服务器。 Python 带有一个简单的开发邮件服务器。 Mailslurper 是易于使用的本地开发服务器。 共享的虚拟主机提供商使我们可以访问邮件服务器。 我们可以在帐户中找到详细信息。

注意:避免使用 Gmail,因为它是高度安全的服务器,并且使其工作非常复杂。 实际上,互联网上的大多数(如果不是全部)示例演示了如何通过 Gmail 服务器发送电子邮件,这些示例都无法正常工作。 而是使用开发服务器或共享的虚拟主机服务器。

最后,我们可以使用 Web 服务。 有开发 Web 服务(例如 MailTrap 或 MailSlurp)或生产服务(例如 Mailgun 或 Mandrill)。

使用 Python 内置邮件服务器

$ python -m smtpd -c DebuggingServer -n localhost:1025

我们在端口 1025 上启动 Python 内置邮件服务器。

built_in.py

#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText

sender = 'admin@example.com'
receivers = ['info@example.com']

port = 1025
msg = MIMEText('This is test mail')

msg['Subject'] = 'Test mail'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

with smtplib.SMTP('localhost', port) as server:

    # server.login('username', 'password')
    server.sendmail(sender, receivers, msg.as_string())
    print("Successfully sent email")

我们向本地开发邮件服务器发送一条简单的文本消息。

sender = 'admin@example.com'
receivers = ['info@example.com']

我们提供发送者和接收者。 example.com是专门用于文档中的说明性示例的域名。

msg = MIMEText('This is test mail')

msg['Subject'] = 'Test mail'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

MimeText用于发送文本电子邮件。 我们提供主题,从选项到选项。

with smtplib.SMTP('localhost', port) as server:
...

SMTP类管理与 SMTP 服务器的连接。

# server.login('username', 'password')

由于我们使用本地开发服务器,因此不必登录。

server.sendmail(sender, receivers, msg.as_string())

电子邮件带有sendmail()发送。

$ python -m smtpd -c DebuggingServer -n localhost:1025
---------- MESSAGE FOLLOWS ----------
b'Content-Type: text/plain; charset="us-ascii"'
b'MIME-Version: 1.0'
b'Content-Transfer-Encoding: 7bit'
b'Subject: Test mail'
b'From: admin@example.com'
b'To: info@example.com'
b'X-Peer: ::1'
b''
b'This is test mail'
------------ END MESSAGE ------------

发送电子邮件后,我们会收到此消息。

发送邮件到 Mailtrap

Mailtrap 提供了一项免费计划,使我们每个月可以发送 500 封邮件。 设置 Mailtrap 非常容易。 如果我们拥有 Github 或 Google 帐户,则只需几秒钟。

设置页面中提供了必要的凭据。 另外,还有一些简短的代码示例显示了如何使用服务,包括smtplibDjangoFlask

mailtrap_simple.py

#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText

sender = 'admin@example.com'
receiver = 'info@example.com'

msg = MIMEText('This is test mail')

msg['Subject'] = 'Test mail'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

user = 'username'
password = 'passoword'

with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:

    server.login(user, password)
    server.sendmail(sender, receiver, msg.as_string())
    print("mail successfully sent")

该示例将简单邮件发送到 Mailtrap 帐户。

server.login(user, password)

用户名和密码在设置页面中给出; 它们由随机字符组成,例如 24h328df3e32。

发送带有附件的电子邮件

当我们有附件或要提供相同内容的替代版本(例如纯文本/ HTML 版本)时,将使用MIMEMultipart

words.txt

falcon
blue
sky
cloud

我们有一个简单的文本文件。

mailtrap_attachment.py

#!/usr/bin/python

import smtplib
from os.path import basename
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication

sender = 'admin@example.com'
receiver = 'info@example.com'

msg = MIMEMultipart()

msg['Subject'] = 'Test mail with attachment'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

filename = 'words.txt'
with open(filename, 'r') as f:
    part = MIMEApplication(f.read(), Name=basename(filename))

part['Content-Disposition'] = 'attachment; filename="{}"'.format(basename(filename))
msg.attach(part)

user = 'username'
password = 'password'

with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:

    server.login(user, password)
    server.sendmail(sender, receiver, msg.as_string())
    print("Successfully sent email")

该示例向 Mailtrap 发送带有文本文件附件的电子邮件。

filename = 'words.txt'
with open(filename, 'r') as f:
    part = MIMEApplication(f.read(), Name=basename(filename))

我们阅读了文本文件的内容。

part['Content-Disposition'] = 'attachment; filename="{}"'.format(basename(filename))
msg.attach(part)

附件通过attach()方法添加。

使用 STARTTLS 的邮件陷阱

Mailtrap 在任何 SMTP 端口上都不支持 SSL,它仅支持STARTTLS。 如果我们尝试使用 SSL,则会收到以下错误消息:

ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1045)

所谓的机会 TLS (传输层安全性)是纯文本通信协议的扩展。 它提供了一种将纯文本连接升级为加密(TLS 或 SSL)连接的方法,而不是使用单独的端口进行加密通信。 为此,一些协议使用名为STARTTLS的命令。 它主要旨在作为被动监视的对策。

mailtrap_secured.py

#!/usr/bin/env python

import smtplib

from email.mime.text import MIMEText

port = 465

sender = 'admin@example.com'
receiver = 'info@example.com'

msg = MIMEText('Secured test mail')

msg['Subject'] = 'Test mail'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

user = 'username'
password = 'password'

with smtplib.SMTP("smtp.mailtrap.io", port) as server:

    server.starttls() # Secure the connection

    server.login(user, password)
    server.sendmail(sender, receiver, msg.as_string())
    print("mail successfully sent")

该示例将电子邮件发送到具有机会性 TLS 的 Mailtrap 帐户。

server.starttls() # Secure the connection

starttls()将与 SMTP 服务器的连接置于 TLS 模式。

通过 SSL 发送邮件

以下示例通过 SSL 发送电子邮件。 使用了 Web 托管 SMTP 服务器(来自 websupport.sk)。

send_mail_ssl.py

#!/usr/bin/env python

import smtplib, ssl
from email.mime.text import MIMEText

sender = 'admin@example.com'
receivers = ['info@example.com']

port = 465
user = 'admin@example.com'
password = 'password'

msg = MIMEText('This is test mail')

msg['Subject'] = 'Test mail'
msg['From'] = 'admin@example.com'
msg['To'] = 'info@example.com'

context = ssl.create_default_context()

with smtplib.SMTP_SSL("smtp.websupport.sk", port, context=context) as server:

    server.login(user, password)
    server.sendmail(sender, receivers, msg.as_string())
    print('mail successfully sent')

SMTP_SSL通过 SSL 加密的套接字连接。

在本教程中,我们使用 Python smtplib模块发送电子邮件。

您可能也对以下相关教程感兴趣: Django 电子邮件教程Python Jinja 教程Python 教程,或列出所有 Python 教程

OpenPyXL 教程

原文: http://zetcode.com/python/openpyxl/

在本教程中,我们展示如何使用 OpenPyXL 库在 Python 中使用 Excel 文件。

OpenPyXL

OpenPyXL 是用于读取和写入 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。

Excel xlsx

在本教程中,我们使用 xlsx 文件。 xlsx 是 Microsoft Excel 使用的开放 XML 电子表格文件格式的文件扩展名。 xlsm 文件支持宏。 xlsx 是专有的二进制格式,而 xlsx 是基于 Office Open XML 格式的。

$ sudo pip3 install openpyxl

我们使用pip3工具安装 OpenPyXL。

OpenPyXL 创建新文件

在第一个示例中,我们使用 OpenPyXL 创建一个新的 xlsx 文件。

write_xlsx.py

#!/usr/bin/env python

from openpyxl import Workbook
import time

book = Workbook()
sheet = book.active

sheet['A1'] = 56
sheet['A2'] = 43

now = time.strftime("%x")
sheet['A3'] = now

book.save("sample.xlsx")

在示例中,我们创建一个新的 xlsx 文件。 我们将数据写入三个单元格。

from openpyxl import Workbook

从 OpenPyXL 模块,我们导入Workbook类。 工作簿是文档所有其他部分的容器。

book = Workbook()

我们创建一个新的工作簿。 始终使用至少一个工作表创建一个工作簿。

sheet = book.active

我们获得对活动工作表的引用。

sheet['A1'] = 56
sheet['A2'] = 43

我们将数值数据写入单元格A1A2

now = time.strftime("%x")
sheet['A3'] = now

我们将当前日期写入单元格A3

book.save("sample.xlsx")

我们使用save()方法将内容写入sample.xlsx文件。

New file

图:新文件

OpenPyXL 写入单元格

写入单元格有两种基本方法:使用工作表的键(例如A1D3),或通过cell()方法使用行和列表示法。

write2cell.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

sheet['A1'] = 1
sheet.cell(row=2, column=2).value = 2

book.save('write2cell.xlsx')

在示例中,我们将两个值写入两个单元格。

sheet['A1'] = 1

在这里,我们将数值分配给A1单元。

sheet.cell(row=2, column=2).value = 2

在这一行中,我们用行和列表示法写入单元格B2

OpenPyXL 附加值

使用append()方法,我们可以在当前工作表的底部附加一组值。

appending_values.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

rows = (
    (88, 46, 57),
    (89, 38, 12),
    (23, 59, 78),
    (56, 21, 98),
    (24, 18, 43),
    (34, 15, 67)
)

for row in rows:
    sheet.append(row)

book.save('appending.xlsx')

在示例中,我们将三列数据附加到当前工作表中。

rows = (
    (88, 46, 57),
    (89, 38, 12),
    (23, 59, 78),
    (56, 21, 98),
    (24, 18, 43),
    (34, 15, 67)
)

数据存储在元组的元组中。

for row in rows:
    sheet.append(row)

我们逐行浏览容器,并使用append()方法插入数据行。

OpenPyXL 读取单元格

在下面的示例中,我们从sample.xlsx文件中读取先前写入的数据。

read_cells.py

#!/usr/bin/env python

import openpyxl

book = openpyxl.load_workbook('sample.xlsx')

sheet = book.active

a1 = sheet['A1']
a2 = sheet['A2']
a3 = sheet.cell(row=3, column=1)

print(a1.value)
print(a2.value) 
print(a3.value)

该示例加载一个现有的 xlsx 文件并读取三个单元格。

book = openpyxl.load_workbook('sample.xlsx')

使用load_workbook()方法打开文件。

a1 = sheet['A1']
a2 = sheet['A2']
a3 = sheet.cell(row=3, column=1)

我们读取A1A2A3单元的内容。 在第三行中,我们使用cell()方法获取A3单元格的值。

$ ./read_cells.py 
56
43
10/26/16

这是示例的输出。

OpenPyXL 读取多个单元格

我们有以下数据表:

Items

图:项目

我们使用范围运算符读取数据。

read_cells2.py

#!/usr/bin/env python

import openpyxl

book = openpyxl.load_workbook('items.xlsx')

sheet = book.active

cells = sheet['A1': 'B6']

for c1, c2 in cells:
    print("{0:8} {1:8}".format(c1.value, c2.value))

在示例中,我们使用范围运算从两列读取数据。

cells = sheet['A1': 'B6']

在这一行中,我们从单元格A1-B6中读取数据。

for c1, c2 in cells:
    print("{0:8} {1:8}".format(c1.value, c2.value))

format()函数用于在控制台上整洁地输出数据。

$ ./read_cells2.py 
Items    Quantity
coins          23
chairs          3
pencils         5
bottles         8
books          30

这是程序的输出。

OpenPyXL 按行迭代

iter_rows()方法将工作表中的单元格返回为行。

iterating_by_rows.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

rows = (
    (88, 46, 57),
    (89, 38, 12),
    (23, 59, 78),
    (56, 21, 98),
    (24, 18, 43),
    (34, 15, 67)
)

for row in rows:
    sheet.append(row)

for row in sheet.iter_rows(min_row=1, min_col=1, max_row=6, max_col=3):
    for cell in row:
        print(cell.value, end=" ")
    print()    

book.save('iterbyrows.xlsx')

该示例逐行遍历数据。

for row in sheet.iter_rows(min_row=1, min_col=1, max_row=6, max_col=3):

我们提供了迭代的边界。

$ ./iterating_by_rows.py 
88 46 57 
89 38 12 
23 59 78 
56 21 98 
24 18 43 
34 15 67 

这是示例的输出。

OpenPyXL 按列迭代

iter_cols()方法将工作表中的单元格作为列返回。

iterating_by_columns.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

rows = (
    (88, 46, 57),
    (89, 38, 12),
    (23, 59, 78),
    (56, 21, 98),
    (24, 18, 43),
    (34, 15, 67)
)

for row in rows:
    sheet.append(row)

for row in sheet.iter_cols(min_row=1, min_col=1, max_row=6, max_col=3):
    for cell in row:
        print(cell.value, end=" ")
    print()    

book.save('iterbycols.xlsx')

该示例逐列遍历数据。

$ ./iterating_by_columns.py 
88 89 23 56 24 34 
46 38 59 21 18 15 
57 12 78 98 43 67 

这是示例的输出。

统计

对于下一个示例,我们需要创建一个包含数字的 xlsx 文件。 例如,我们使用RANDBETWEEN()函数在 10 列中创建了 25 行数字。

mystats.py

#!/usr/bin/env python

import openpyxl
import statistics as stats

book = openpyxl.load_workbook('numbers.xlsx', data_only=True)

sheet = book.active

rows = sheet.rows

values = []

for row in rows:
    for cell in row:
        values.append(cell.value)

print("Number of values: {0}".format(len(values)))
print("Sum of values: {0}".format(sum(values)))
print("Minimum value: {0}".format(min(values)))
print("Maximum value: {0}".format(max(values)))
print("Mean: {0}".format(stats.mean(values)))
print("Median: {0}".format(stats.median(values)))
print("Standard deviation: {0}".format(stats.stdev(values)))
print("Variance: {0}".format(stats.variance(values)))

在示例中,我们从工作表中读取所有值并计算一些基本统计信息。

import statistics as stats

导入statistics模块以提供一些统计函数,例如中值和方差。

book = openpyxl.load_workbook('numbers.xlsx', data_only=True)

使用data_only选项,我们从单元格而不是公式中获取值。

rows = sheet.rows

我们得到所有不为空的单元格行。

for row in rows:
    for cell in row:
        values.append(cell.value)

在两个for循环中,我们从单元格中形成一个整数值列表。

print("Number of values: {0}".format(len(values)))
print("Sum of values: {0}".format(sum(values)))
print("Minimum value: {0}".format(min(values)))
print("Maximum value: {0}".format(max(values)))
print("Mean: {0}".format(stats.mean(values)))
print("Median: {0}".format(stats.median(values)))
print("Standard deviation: {0}".format(stats.stdev(values)))
print("Variance: {0}".format(stats.variance(values)))

我们计算并打印有关值的数学统计信息。 一些函数是内置的,其他函数是通过statistics模块导入的。

$ ./mystats.py 
Number of values: 312
Sum of values: 15877
Minimum value: 0
Maximum value: 100
Mean: 50.88782051282051
Median: 54.0
Standard deviation: 28.459203819700967
Variance: 809.9262820512821

这是一个示例输出。

OpenPyXL 过滤器&排序数据

图纸具有auto_filter属性,该属性允许设置过滤条件和排序条件。

请注意,OpenPyXL 设置了条件,但是我们必须在电子表格应用中应用它们。

filter_sort.py

#!/usr/bin/env python

from openpyxl import Workbook

wb = Workbook()
sheet = wb.active

data = [
    ['Item', 'Colour'],
    ['pen', 'brown'],
    ['book', 'black'],
    ['plate', 'white'],
    ['chair', 'brown'],
    ['coin', 'gold'],
    ['bed', 'brown'],
    ['notebook', 'white'],
]

for r in data:
    sheet.append(r)

sheet.auto_filter.ref = 'A1:B8'
sheet.auto_filter.add_filter_column(1, ['brown', 'white'])
sheet.auto_filter.add_sort_condition('B2:B8')

wb.save('filtered.xlsx')

在示例中,我们创建一个包含项目及其颜色的工作表。 我们设置一个过滤器和一个排序条件。

OpenPyXL 维度

为了获得那些实际包含数据的单元格,我们可以使用维度。

dimensions.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

sheet['A3'] = 39
sheet['B3'] = 19

rows = [
    (88, 46),
    (89, 38),
    (23, 59),
    (56, 21),
    (24, 18),
    (34, 15)
]

for row in rows:
    sheet.append(row)

print(sheet.dimensions)
print("Minimum row: {0}".format(sheet.min_row))
print("Maximum row: {0}".format(sheet.max_row))
print("Minimum column: {0}".format(sheet.min_column))
print("Maximum column: {0}".format(sheet.max_column))

for c1, c2 in sheet[sheet.dimensions]:
    print(c1.value, c2.value)

book.save('dimensions.xlsx')

该示例计算两列数据的维数。

sheet['A3'] = 39
sheet['B3'] = 19

rows = [
    (88, 46),
    (89, 38),
    (23, 59),
    (56, 21),
    (24, 18),
    (34, 15)
]

for row in rows:
    sheet.append(row)

我们将数据添加到工作表。 请注意,我们从第三行开始添加。

print(sheet.dimensions)

dimensions属性返回非空单元格区域的左上角和右下角单元格。

print("Minimum row: {0}".format(sheet.min_row))
print("Maximum row: {0}".format(sheet.max_row))

使用min_rowmax_row属性,我们可以获得包含数据的最小和最大行。

print("Minimum column: {0}".format(sheet.min_column))
print("Maximum column: {0}".format(sheet.max_column))

通过min_columnmax_column属性,我们获得了包含数据的最小和最大列。

for c1, c2 in sheet[sheet.dimensions]:
    print(c1.value, c2.value)

我们遍历数据并将其打印到控制台。

$ ./dimensions.py 
A3:B9
Minimum row: 3
Maximum row: 9
Minimum column: 1
Maximum column: 2
39 19
88 46
89 38
23 59
56 21
24 18
34 15

这是示例的输出。

工作表

每个工作簿可以有多个工作表。

Sheets

图:床单

让我们有一张包含这三张纸的工作簿。

sheets.py

#!/usr/bin/env python

import openpyxl

book = openpyxl.load_workbook('sheets.xlsx')

print(book.get_sheet_names())

active_sheet = book.active
print(type(active_sheet))

sheet = book.get_sheet_by_name("March")
print(sheet.title)

该程序可用于 Excel 工作表。

print(book.get_sheet_names())

get_sheet_names()方法返回工作簿中可用工作表的名称。

active_sheet = book.active
print(type(active_sheet))

我们获取活动表并将其类型打印到终端。

sheet = book.get_sheet_by_name("March")

我们使用get_sheet_by_name()方法获得对工作表的引用。

print(sheet.title)

检索到的工作表的标题将打印到终端。

$ ./sheets.py 
['January', 'February', 'March']
<class 'openpyxl.worksheet.worksheet.Worksheet'>
March

这是程序的输出。

sheets2.py

#!/usr/bin/env python

import openpyxl

book = openpyxl.load_workbook('sheets.xlsx')

book.create_sheet("April")

print(book.sheetnames)

sheet1 = book.get_sheet_by_name("January")
book.remove_sheet(sheet1)

print(book.sheetnames)

book.create_sheet("January", 0)
print(book.sheetnames)

book.save('sheets2.xlsx')

在此示例中,我们创建一个新工作表。

book.create_sheet("April")

使用create_sheet()方法创建一个新图纸。

print(book.sheetnames)

图纸名称也可以使用sheetnames属性显示。

book.remove_sheet(sheet1)

可以使用remove_sheet()方法将纸张取出。

book.create_sheet("January", 0)

可以在指定位置创建一个新图纸。 在我们的例子中,我们在索引为 0 的位置创建一个新工作表。

$ ./sheets2.py 
['January', 'February', 'March', 'April']
['February', 'March', 'April']
['January', 'February', 'March', 'April']

这是程序的输出。

可以更改工作表的背景颜色。

sheets3.py

#!/usr/bin/env python

import openpyxl

book = openpyxl.load_workbook('sheets.xlsx')

sheet = book.get_sheet_by_name("March")
sheet.sheet_properties.tabColor = "0072BA"

book.save('sheets3.xlsx')

该示例修改了标题为"March"的工作表的背景颜色。

sheet.sheet_properties.tabColor = "0072BA"

我们将tabColor属性更改为新颜色。

Background colour of a worksheet

图:工作表的背景色

第三工作表的背景色已更改为某种蓝色。

合并单元格

单元格可以使用merge_cells()方法合并,而可以不使用unmerge_cells()方法合并。 当我们合并单元格时,除了左上角的所有单元格都将从工作​​表中删除。

merging_cells.py

#!/usr/bin/env python

from openpyxl import Workbook
from openpyxl.styles import Alignment

book = Workbook()
sheet = book.active

sheet.merge_cells('A1:B2')

cell = sheet.cell(row=1, column=1)
cell.value = 'Sunny day'
cell.alignment = Alignment(horizontal='center', vertical='center')

book.save('merging.xlsx')

在该示例中,我们合并了四个单元格:A1B1A2B2。 最后一个单元格中的文本居中。

from openpyxl.styles import Alignment

为了使文本在最后一个单元格中居中,我们使用了openpyxl.styles模块中的Alignment类。

sheet.merge_cells('A1:B2')

我们用merge_cells()方法合并四个单元格。

cell = sheet.cell(row=1, column=1)

我们得到了最后一个单元格。

cell.value = 'Sunny day'
cell.alignment = Alignment(horizontal='center', vertical='center')

我们将文本设置为合并的单元格并更新其对齐方式。

Merged cells

图:合并的单元格

OpenPyXL 冻结窗格

冻结窗格时,在滚动到工作表的另一个区域时,我们会保持工作表的某个区域可见。

freezing.py

#!/usr/bin/env python

from openpyxl import Workbook
from openpyxl.styles import Alignment

book = Workbook()
sheet = book.active

sheet.freeze_panes = 'B2'

book.save('freezing.xlsx')

该示例通过单元格B2冻结窗格。

sheet.freeze_panes = 'B2'

要冻结窗格,我们使用freeze_panes属性。

OpenPyXL 公式

下一个示例显示如何使用公式。 OpenPyXL 不进行计算; 它将公式写入单元格。

formulas.py

#!/usr/bin/env python

from openpyxl import Workbook

book = Workbook()
sheet = book.active

rows = (
    (34, 26),
    (88, 36),
    (24, 29),
    (15, 22),
    (56, 13),
    (76, 18)
)

for row in rows:
    sheet.append(row)

cell = sheet.cell(row=7, column=2)
cell.value = "=SUM(A1:B6)"
cell.font = cell.font.copy(bold=True)

book.save('formulas.xlsx')

在示例中,我们使用SUM()函数计算所有值的总和,并以粗体显示输出样式。

rows = (
    (34, 26),
    (88, 36),
    (24, 29),
    (15, 22),
    (56, 13),
    (76, 18)
)

for row in rows:
    sheet.append(row)

我们创建两列数据。

cell = sheet.cell(row=7, column=2)

我们得到显示计算结果的单元格。

cell.value = "=SUM(A1:B6)"

我们将一个公式写入单元格。

cell.font = cell.font.copy(bold=True)

我们更改字体样式。

Calculating the sum of values

图:计算值之和

OpenPyXL 图像

在下面的示例中,我们显示了如何将图像插入到工作表中。

write_image.py

#!/usr/bin/env python

from openpyxl import Workbook
from openpyxl.drawing.image import Image

book = Workbook()
sheet = book.active

img = Image("icesid.png")
sheet['A1'] = 'This is Sid'

sheet.add_image(img, 'B2')

book.save("sheet_image.xlsx")

在示例中,我们将图像写到一张纸上。

from openpyxl.drawing.image import Image

我们使用openpyxl.drawing.image模块中的Image类。

img = Image("icesid.png")

创建一个新的Image类。 icesid.png图像位于当前工作目录中。

sheet.add_image(img, 'B2')

我们使用add_image()方法添加新图像。

OpenPyXL 图表

OpenPyXL 库支持创建各种图表,包括条形图,折线图,面积图,气泡图,散点图和饼图。

根据文档,OpenPyXL 仅支持在工作表中创建图表。 现有工作簿中的图表将丢失。

create_bar_chart.py

#!/usr/bin/env python

from openpyxl import Workbook
from openpyxl.chart import (
    Reference,
    Series,
    BarChart
)

book = Workbook()
sheet = book.active

rows = [
    ("USA", 46),
    ("China", 38),
    ("UK", 29),
    ("Russia", 22),
    ("South Korea", 13),
    ("Germany", 11)
]

for row in rows:
    sheet.append(row)

data = Reference(sheet, min_col=2, min_row=1, max_col=2, max_row=6)
categs = Reference(sheet, min_col=1, min_row=1, max_row=6)

chart = BarChart()
chart.add_data(data=data)
chart.set_categories(categs)

chart.legend = None
chart.y_axis.majorGridlines = None
chart.varyColors = True
chart.title = "Olympic Gold medals in London"

sheet.add_chart(chart, "A8")    

book.save("bar_chart.xlsx")

在此示例中,我们创建了一个条形图,以显示 2012 年伦敦每个国家/地区的奥运金牌数量。

from openpyxl.chart import (
    Reference,
    Series,
    BarChart
)

openpyxl.chart模块具有使用图表的工具。

book = Workbook()
sheet = book.active

创建一个新的工作簿。

rows = [
    ("USA", 46),
    ("China", 38),
    ("UK", 29),
    ("Russia", 22),
    ("South Korea", 13),
    ("Germany", 11)
]

for row in rows:
    sheet.append(row)

我们创建一些数据并将其添加到活动工作表的单元格中。

data = Reference(sheet, min_col=2, min_row=1, max_col=2, max_row=6)

对于Reference类,我们引用表中代表数据的行。 在我们的案例中,这些是奥运金牌的数量。

categs = Reference(sheet, min_col=1, min_row=1, max_row=6)

我们创建一个类别轴。 类别轴是将数据视为一系列非数字文本标签的轴。 在我们的案例中,我们有代表国家名称的文本标签。

chart = BarChart()
chart.add_data(data=data)
chart.set_categories(categs)

我们创建一个条形图并为其设置数据和类别。

chart.legend = None
chart.y_axis.majorGridlines = None

使用legendmajorGridlines属性,可以关闭图例和主要网格线。

chart.varyColors = True

varyColors设置为True,每个条形都有不同的颜色。

chart.title = "Olympic Gold medals in London"

为图表设置标题。

sheet.add_chart(chart, "A8")   

使用add_chart()方法将创建的图表添加到工作表中。

Bar chart

图:条形图

在本教程中,我们使用了 OpenPyXL 库。 我们已经从 Excel 文件中读取数据,并将数据写入 Excel 文件中。

您可能也对以下相关教程感兴趣: Python 教程Python CSV 教程Python SimpleJson 教程Python 列表推导

列出所有 Python 教程

Python pathlib教程

原文: http://zetcode.com/python/pathlib/

Python pathlib教程展示了如何通过pathlib模块使用 Python 中的文件和目录。

pathlib是一个 Python 模块,提供用于处理文件和目录的对象 API。 pathlib是标准模块。

Path是使用文件的核心对象。

$ pip install prettytable
$ pip install more_itertools

在本教程中,我们还将使用prettytablemore_itertools

words.txt

blue
forest
sky
ocean
rabbit
clue

一些示例使用此简单的文本文件。

Path.cwdhome

我们通过cwd()获得当前工作目录,并通过home()获得主目录。

cwd_home.py

#!/usr/bin/env python

from pathlib import Path

print(f"Current directory: {Path.cwd()}")
print(f"Home directory: {Path.home()}")

该示例打印当前的工作主管和主目录。

$ cwd_home.py
Current directory: C:\Users\Jano\Documents\pyprogs\pathlib
Home directory: C:\Users\Jano

这是一个示例输出。

变更目录

我们使用os' chdir()进入另一个目录。

change_dir.py

#!/usr/bin/env python

from pathlib import Path
from os import chdir

path = Path('..')

print(f'Current working directory: {path.cwd()}')

chdir(path)

print(f'Current working directory: {path.cwd()}')

chdir('..')

我们更改当前的工作目录。 请注意,仅在 Python 程序内部更改目录。

$ change_dir.py
Current working directory: C:\Users\Jano\Documents\pyprogs\pathlib
Current working directory: C:\Users\Jano\Documents\pyprogs

这是一个示例输出。

Path.mkdir

使用mkdir()创建一个新目录。

mkdir.py

#!/usr/bin/env python

from pathlib import Path

path = Path.cwd() / 'new'

path.mkdir()

该示例在当前工作目录内创建一个新目录。

复制文件

借助shutil模块,我们复制了一个文件。

copy_file.py

#!/usr/bin/env python

from pathlib import Path
from shutil import copyfile

source = Path('words.txt')
destination = Path('words_bck.txt')

copyfile(source, destination)

该示例复制了words.txt文件。

source = Path('words.txt')

通过将文件名传递给Path构造器来创建文件对象。

连接路径

路径可以用/运算符或joinpath()方法连接。

join_path.py

#!/usr/bin/env python

from pathlib import Path

path = Path.home()

docs = path / 'Documents'
pictures = path / 'Pictures'

print(docs)
print(pictures)

在示例中,我们使用/将两条路径连接在一起。

$ join_path.py
C:\Users\Jano\Documents
C:\Users\Jano\Pictures

这是输出。

Path.touch

touch()创建一个新的空文件; 它等效于 Linux touch 命令。

touch.py

#!/usr/bin/python3

from pathlib import Path

Path('myfile.txt').touch()

我们创建一个新的空myfile.txt

Path.rename

rename()重命名文件或目录。

rename.py

#!/usr/bin/env python

from pathlib import Path

path = Path('names.txt')

path.rename('mynames.txt')

该示例将当前工作目录中的names.txt重命名为mynames.txt

路径名

我们使用绝对文件路径或相对路径来引用文件。 路径具有不同的表示形式。 Windows 使用与 Linux 不同的文件路径。

path_names.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Downloads/wordpress-5.1.tar.gz')

print(path)
print(path.as_uri())
print(path.as_posix())

该示例显示了三种不同的文件路径结构。

$ path_names.py
C:\Users\Jano\Downloads\wordpress-5.1.tar.gz
file:///C:/Users/Jano/Downloads/wordpress-5.1.tar.gz
C:/Users/Jano/Downloads/wordpress-5.1.tar.gz

第一个是 Windows 文件路径。 第二个是 URI 样式。 第三个是 POSIX 样式。

相对路径

相对路径从某个给定的工作目录开始,从而避免了提供完整的绝对路径的需要。 例如,从/home/users/jano/目录的角度来看,data.txt/home/users/jano/data.txt的相对路径。

换句话说,当我们位于/home/users/jano/目录中时,我们可以仅通过文件名data.txt来关联该文件,而无需指定完整路径。

relative_path.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Downloads/wordpress-5.1.tar.gz')

home = Path.home()

relative = path.relative_to(home)
print(relative)

该示例在给定主目录的情况下打印存档文件的相对路径。

$ relative_path.py
Downloads\wordpress-5.1.tar.gz

这是输出。

Path.parent

使用parent()parents(),我们可以获得路径的逻辑父级。

parents.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Documents')

print(f"The parent directory of {path} is {path.parent}")
print(f"The parent of the parent of {path} is {path.parent.parent}")

print(f"All the parents of {path.parent}: ")

print(list(path.parents))

该示例打印路径的父级。

print(f"The parent of the parent of {path} is {path.parent.parent}")

我们可以得到父级的父级。

$ parents.py
The parent directory of C:\Users\Jano\Documents is C:\Users\Jano
The parent of the parent of C:\Users\Jano\Documents is C:\Users
All the parents of C:\Users\Jano:
[WindowsPath('C:/Users/Jano'), WindowsPath('C:/Users'), WindowsPath('C:/')]

这是输出。

路径子元素

路径由子元素组成,例如驱动器或根。

parts.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Documents')

print(path.parts)
print(path.drive)
print(path.root)

该程序将打印路径的某些部分。

print(path.parts)

通过parts,可以访问路径的各种组件。

print(path.drive)

drive给出一个代表驱动器号或名称的字符串(如果有)。

print(path.root)

root给出一个表示(本地或全局)根的字符串(如果有)。

$ parts.py
('C:\\', 'Users', 'Jano', 'Documents')
C:
\

这是输出。

以下程序给出了路径的其他部分。

parts2.py

#!/usr/bin/env python

from pathlib import Path
import os

path = Path('C:/Users/Jano/Downloads/wordpress-5.1.tar.gz')

print(f"The stem is: {path.stem}")
print(f"The name is: {path.name}")
print(f"The suffix is: {path.suffix}")
print(f"The anchor is: {path.anchor}")

print(f"File name: {os.path.splitext(path.stem)[0]}")

print("The suffixes: ")
print(path.suffixes)

该程序将打印词干,名称,后缀和锚点。

$ parts2.py
The stem is: wordpress-5.1.tar
The name is: wordpress-5.1.tar.gz
The suffix is: .gz
The anchor is: C:\
File name: wordpress-5.1
The suffixes:
['.1', '.tar', '.gz']

这是输出。

Path.iterdir

iterdir()产生目录内容的路径对象。

list_dirs.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Documents')

dirs = [e for e in path.iterdir() if e.is_dir()]
print(dirs)

该示例打印指定目录的子目录。 我们检查路径对象是否为is_dir()目录。

以下示例在指定目录内打印文件。

list_files.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Documents')

files = [e for e in path.iterdir() if e.is_file()]
print(files)

我们检查路径对象是否为is_file()文件。

路径遍历

球形模式使用通配符指定文件名集。 例如,*.txt表示所有名称以.txt结尾的文件。 *是代表任何字符串的通配符。 另一个常见的通配符是问号(?),代表一个字符。

路径提供glob()rglob()。 后者用于递归glob。 它将**/添加到给定模式的前面。

globbing.py

#!/usr/bin/env python

from pathlib import Path

path = Path('C:/Users/Jano/Documents/pyprogs')

for e in path.rglob('*.py'):
    print(e)

# for e in path.glob('**/*.py'):
#     print(e)    

该示例打印指定目录及其所有子目录中的所有 Python 文件。 请注意,此类操作可能非常耗时。

for e in path.rglob('*.py'):
print(e)

# for e in path.glob('**/*.py'):
#     print(e)    

这两个操作是等效的。

路径树

以下示例是一个工具,它以分层树结构输出指定目录的内容。

tree.py

#!/usr/bin/env python

from pathlib import Path

def tree(directory):

    print(f'+ {directory}')

    for path in sorted(directory.rglob('*')):

        depth = len(path.relative_to(directory).parts)
        spacer = '    ' * depth

        # print(f'{spacer}+ {path.name}')

        if path.is_file():
            print(f'{spacer}f {path.name}')
        else:
            print(f'{spacer}d {path.name}')

path = Path.home() / 'Downloads'

tree(path)

该程序以树形结构输出Downloads目录的内容。

按扩展名计数文件

在以下示例中,我们按扩展名对所有文件进行计数。 我们将collectionsCounter用于任务。

count_files.py

#!/usr/bin/env python

import collections
from pathlib import Path

docs = Path.home() / 'Documents'

files = [path.suffix for path in docs.iterdir() if path.is_file() and path.suffix]
data = collections.Counter(files)

print(data)

for key, val in data.items(): 
    print(f'{key}: {val}')

该示例对在Documents目录中按扩展名分组的文件进行计数。

files = [path.suffix for path in docs.iterdir() if path.is_file() and path.suffix]

在列表推导式中,我们确保路径对象是带有is_file()的文件,并且该文件具有en扩展名。 文件可能没有扩展名。 特别是在 Unix 系统上。

$ count_files.py
Counter({'.txt': 7, '.pdf': 3, '.ini': 1, '.zip': 1, '.rtf': 1})
.pdf: 3
.txt: 7
.ini: 1
.zip: 1
.rtf: 1

这是一个示例输出。

Path.read_text

read_text()以字符串形式读取文件的内容。 该文件被打开,然后关闭。 可选参数的含义与open()中的含义相同。

read_text.py

#!/usr/bin/env python

from pathlib import Path

path = Path('words.txt')

content = path.read_text()
print(content)

该示例使用read_text()读取words.txt文件的内容。

$ read_text.py
blue
forest
sky
ocean
rabbit
clue

这是输出。

Path.open读取文件

open()会打开路径指向的文件,就像内置的open()函数一样。

read_with_open.py

#!/usr/bin/env python

from pathlib import Path

path = Path('words.txt')

with path.open() as f: 
    lines = f.readlines()
    print(lines)

for line in lines:
    print(line.rstrip())

该示例使用open()打开words.txt文件,并使用readlines()读取内容。

Path读取二进制文件

可以使用read_bytes()读取图像等二进制文件。

read_bytes.py

#!/usr/bin/env python

from pathlib import Path
import binascii
from more_itertools import sliced

path = Path('sid.jpg')

hexed = binascii.hexlify(path.read_bytes())
mybytes = list(sliced(hexed, 2))

i = 0

for b in mybytes:

    print(b.decode("utf-8") , end=' ')
    i += 1

    if (i % 30 == 0):
        print()

该示例读取 JPEG 图片并将其以十六进制表示形式打印到终端。

路径write_text

write_text以文本模式打开文件,向其中写入数据,然后关闭文件。

write_text.py

#!/usr/bin/python3

from pathlib import Path

path = Path('myfile.txt')
path.touch()

path.write_text('This is myfile.txt')

该示例使用touch()创建一个新的空文件,并使用write_text()将一些文本数据写入该文件。

新文章

内容管理系统通常根据当年和月份将其新创建的文章放在目录结构中。 下一个示例对此进行了演示。

new_article.py

#!/usr/bin/env python

from pathlib import Path
import datetime

now = datetime.datetime.now()
year = now.year
month = now.month

name = input('Enter article name:')

path1 = Path('articles') / str(year) / str(month)
path1.mkdir(parents=True)

path2 = path1 /  f'{name}.txt'

path2.touch()

print(f'Article created at: {path2}')

该程序要求用户输入。 它根据当前年份和月份创建一个新的文本文件。

PrettyTable示例

处理文件和目录时,可以使用PrettyTable模块获得更好的输出。

simple_table.py

#!/usr/bin/env python

from pathlib import Path
import datetime
from prettytable import PrettyTable

path = Path('C:/Users/Jano/Documents/')

pt = PrettyTable()
pt.field_names = ["File name", "Size", "Created"]

pt.align["File name"] = "l"
pt.align["Size"] = "r"
pt.align["Created"] = "l"

for e in path.glob('**/*.txt'):

    created = datetime.datetime.fromtimestamp(e.stat().st_ctime)
    size = e.stat().st_size
    pt.add_row([e.name, size, f"{created:%Y-%m-%d}"])

print(pt)

该示例在一个漂亮的表中显示Documents内的所有文本文件。 该表包含三列:文件名,大小和创建日期。

$ simple_table.py
+-------------------------------------------------------+-------+------------+
| File name                                             |  Size | Created    |
+-------------------------------------------------------+-------+------------+
| data.txt                                              |     0 | 2019-02-27 |
| eternal_return.txt                                    | 10467 | 2019-03-03 |
| potvrdenie.txt                                        |   773 | 2019-01-14 |
| text_processing.txt                                   |   750 | 2019-02-18 |
| website-inspire.txt                                   |    62 | 2019-03-03 |
| words.txt                                             |    31 | 2018-12-30 |
| Úvod do Symfony.txt                                   |  7613 | 2019-03-04 |
| robots.txt                                            |   240 | 2019-01-01 |
| robots.txt                                            |   240 | 2019-02-03 |
...

这是样本部分输出。

在本教程中,我们介绍了标准的 Python pathlib模块。

您可能也对以下相关教程感兴趣: PrettyTable 教程Python argparse教程Python 教程

Python YAML 教程

原文: http://zetcode.com/python/yaml/

Python YAML 教程展示了如何在 Python 中使用 YAML 格式。 我们使用 PyYAML 模块。

YAML 格式

YAML(不是 YAML 标记语言)是一种人类可读的数据序列化语言。 它通常用于配置文件,但也用于数据存储(例如调试输出)或传输(例如文档标题)。

YAML 本机支持三种基本数据类型:标量(例如字符串,整数和浮点数),列表和关联数组。

官方推荐的 YAML 文件扩展名是.yaml。 Python 中有两个用于 YAML 的模块:PyYAML 和ruamel.yaml。 在本教程中,我们使用前者。

PyYAML

PyYAML 是 Python 的 YAML 解析器和发射器。

$ pip install pyyaml

该模块通过 pip 安装。

YAML 文件

在本教程中,我们使用以下 YAML 文件:

items.yaml

raincoat: 1
coins: 5
books: 23
spectacles: 2
chairs: 12
pens: 6

我们有几个标量值。

data.yaml

cities:
  - Bratislava
  - Kosice
  - Trnava
  - Moldava
  - Trencin
---
companies:
  - Eset
  - Slovnaft
  - Duslo Sala
  - Matador Puchov

我们在data.yaml中有两个文档。 文件用---分隔。

Python YAML 读取

在第一个示例中,我们读取了一个 YAML 文件。

read_yaml.py

#!/usr/bin/env python3

import yaml

with open('items.yaml') as f:

    data = yaml.load(f, Loader=yaml.FullLoader)
    print(data)

我们打开items.yaml文件,并使用yaml.load()方法加载内容。 数据被打印到控制台。

$ python read_yaml.py
{'raincoat': 1, 'coins': 5, 'books': 23, 'spectacles': 2, 'chairs': 12, 'pens': 6}

PyYAML 模块将标量值转换为 Python 字典。

Python YAML 阅读文档

使用load_all()读取多个 YAML 文档。

read_docs.py

#!/usr/bin/env python3

import yaml

with open('data.yaml') as f:

    docs = yaml.load_all(f, Loader=yaml.FullLoader)

    for doc in docs:

        for k, v in doc.items():
            print(k, "->", v)

该示例从data.yaml文件中读取两个文档。

$ python read_docs.py
cities -> ['Bratislava', 'Kosice', 'Trnava', 'Moldava', 'Trencin']
companies -> ['Eset', 'Slovnaft', 'Duslo Sala', 'Matador Puchov']

这是输出。

Python YAML 转储

dump()方法将 Python 对象序列化为 YAML 流。

dumping.py

#!/usr/bin/env python3

import yaml

users = [{'name': 'John Doe', 'occupation': 'gardener'},
         {'name': 'Lucy Black', 'occupation': 'teacher'}]

print(yaml.dump(users))

在示例中,我们有一个字典列表。 我们使用dump()方法将列表序列化为 YAML 格式。

$ python dumping.py
- name: John Doe
  occupation: gardener
- name: Lucy Black
  occupation: teacher

这是输出。

Python YAML 写入

以下示例将 Python 数据写入 YAML 文件。

writing.py

#!/usr/bin/env python3

import yaml

users = [{'name': 'John Doe', 'occupation': 'gardener'},
         {'name': 'Lucy Black', 'occupation': 'teacher'}]

with open('users.yaml', 'w') as f:

    data = yaml.dump(users, f)

该示例将字典列表写入users.yaml文件。

data = yaml.dump(users, f)

我们使用dump()方法写入数据。 第一个参数是数据,第二个参数是文件对象。

Python YAML 排序键

我们可以使用dumpsort_keys参数对键进行排序。

sort_keys.py

#!/usr/bin/env python3

import yaml

with open('items.yaml') as f:

    data = yaml.load(f, Loader=yaml.FullLoader)
    print(data)

    sorted = yaml.dump(data, sort_keys=True)
    print(sorted)

该示例从items.yaml文件中读取数据,并通过 YAML 输出中的键对数据进行排序。

$ python sort_keys.py
{'raincoat': 1, 'coins': 5, 'books': 23, 'spectacles': 2, 'chairs': 12, 'pens': 6}
books: 23
chairs: 12
coins: 5
pens: 6
raincoat: 1
spectacles: 2

这是输出。

记号

解析 YAML 文件时,我们可以使用较低级别的 API。 scan()方法扫描 YAML 流并生成扫描记号。

tokens.py

#!/usr/bin/env python3

import yaml

with open('items.yaml') as f:

    data = yaml.scan(f, Loader=yaml.FullLoader)

    for token in data:
        print(token)

该示例扫描 YAML 文件并打印记号。

$ python tokens.py
StreamStartToken(encoding=None)
BlockMappingStartToken()
KeyToken()
ScalarToken(plain=True, style=None, value='raincoat')
ValueToken()
ScalarToken(plain=True, style=None, value='1')
KeyToken()
ScalarToken(plain=True, style=None, value='coins')
ValueToken()
ScalarToken(plain=True, style=None, value='5')
KeyToken()
ScalarToken(plain=True, style=None, value='books')
ValueToken()
ScalarToken(plain=True, style=None, value='23')
KeyToken()
ScalarToken(plain=True, style=None, value='spectacles')
ValueToken()
ScalarToken(plain=True, style=None, value='2')
KeyToken()
ScalarToken(plain=True, style=None, value='chairs')
ValueToken()
ScalarToken(plain=True, style=None, value='12')
KeyToken()
ScalarToken(plain=True, style=None, value='pens')
ValueToken()
ScalarToken(plain=True, style=None, value='6')
BlockEndToken()
StreamEndToken()

这是输出。

在本教程中,我们使用 Python 中的 YAML 格式。

您可能也对以下相关教程感兴趣: Python 字符串Python Jinja 教程Python 教程,或列出所有 Python 教程

Python 哈希教程

原文: http://zetcode.com/python/hashing/

Python 哈希教程解释了 Python 中的哈希概念。 我们介绍了哈希表和 Python 可哈希对象。

哈希表

哈希表用于以许多常见的编程语言(例如 C++ ,Java 和 Python)实现映射和设置数据结构。 Python 将哈希表用于字典和集合。 哈希表是键值对的无序集合,其中每个键都是唯一的。 哈希表提供了有效的查找,插入和删除操作的组合。 这些是数组和链表的最佳属性。

哈希

哈希是使用算法将任意大小的数据映射到固定长度的过程。 这称为哈希值。 哈希用于创建高性能,直接访问的数据结构,在该结构中要快速存储和访问大量数据。 哈希值使用哈希函数计算。

Python 可哈希对象

如果对象的哈希值在其生命周期内从未发生变化,则该对象是可哈希的。 (在多次调用 Python 程序期间,它可以具有不同的值。)可哈希对象需要__hash__()方法。 为了执行比较,哈希需要一种__eq__()方法。

注意:比较相等的可哈希对象必须具有相同的哈希值。

哈希性使对象可用作字典键和集成员,因为这些数据结构在内部使用哈希值。 Python 不可变的内置对象是可哈希的; 可变容器(例如列表或字典)不是。 默认情况下,作为用户定义类实例的对象是可哈希的。 它们都比较不相等(除了它们本身),并且它们的哈希值是从id()派生的。

注意:如果一个类没有定义一个__eq __()方法,它也不应该定义一个__hash __()操作。 如果它定义了__eq __()而不是__hash __(),则其实例将不能用作可哈希集合中的项目。

Python hash()函数

hash()函数返回对象的哈希值(如果有的话)。 哈希值是整数。 它们用于在字典查找期间快速比较字典关键字。 对象可以实现__hash__()方法。

Python 不可变内置函数可哈希化

Python 不变的内置函数(例如整数,字符串或元组)是可哈希的。

builtin_hashables.py

#!/usr/bin/env python3

val = 100

print(val.__hash__())
print("falcon".__hash__())
print((1,).__hash__())

该示例显示三个哈希值的值:整数,字符串和元组。

Python 自定义可哈希对象示例 I

Python 自定义对象默认情况下是可哈希的。 他们的哈希值是从其 ID 派生的。

custom_object.py

#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

print('hash of user 1')
print(hash(u1))

print('hash of user 2')
print(hash(u2))

if (u1 == u2):
    print('same user')
else:
    print('different users')

在示例中,我们有User的两个实例。

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

我们有两个具有相同数据的实例。

print('hash of user 1')
print(hash(u1))

hash()函数返回对象的哈希值。 默认实现是从对象的 ID 派生的。

$ python custom_object.py
hash of user 1
-9223371894419573195
hash of user 2
142435202673
different users

即使用户详细信息相同,但比较仍会产生不同的对象。 为了更改它,我们需要实现__eq__()方法。

Python 自定义可哈希对象示例 II

在第二个示例中,我们实现了自定义__eq__()方法。

custom_object2.py

#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __eq__(self, other):

        return self.name == other.name \
            and self.occupation == other.occupation

    def __str__(self):
        return f'{self.name} {self.occupation}'

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

# users = {u1, u2}
# print(len(users))

现在比较返回给我们的预期输出; 但是,我们不能将对象插入 Python 集中; 这将导致TypeError: unhashable type: 'User'。 为了更改此设置,我们实现了__hash__()方法。

Python 自定义可哈希对象示例 III

在第三个示例中,我们实现了__eq__()__hash__()方法。

custom_object3.py

#!/usr/bin/env python3

class User:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __eq__(self, other):

        return self.name == other.name \
            and self.occupation == other.occupation

    def __hash__(self):
        return hash((self.name, self.occupation))

    def __str__(self):
        return f'{self.name} {self.occupation}'

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

users = {u1, u2}

print(len(users))

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

print('------------------------------------')

u1.occupation = 'programmer'

users = {u1, u2}

print(len(users))

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

该示例比较了具有__eq__()__hash__()方法的自定义实现的两个对象。 可以将这些对象插入 Python 集中,当以后更改属性时,我们将获得预期的输出。

def __hash__(self):
    return hash((self.name, self.occupation))

__hash__()函数的实现从属性元组返回使用hash()函数计算的哈希值。

$ python custom_object3.py
1
same user
John Doe gardener == John Doe gardener
------------------------------------
2
different users

这是输出。

Python @dataclass装饰器

从 Python 3.7 开始,我们有了dataclass装饰器,它会自动生成一些样板代码。

数据类装饰器的冻结参数(默认为False)。 如果指定,则字段将被冻结(即只读)。 如果eq设置为True(默认情况下),则将实现__hash__()方法,并且对象实例将是可哈希的。

decorator.py

#!/usr/bin/env python3

from dataclasses import dataclass

@dataclass(frozen=True)
class User:

    name: str
    occupation: str

u1 = User('John Doe', 'gardener')
u2 = User('John Doe', 'gardener')

if (u1 == u2):
    print('same user')
    print(f'{u1} == {u2}')
else:
    print('different users')

users = {u1, u2}
print(len(users))

该示例使用@dataclass装饰器。

$ python decorator.py
same user
User(name='John Doe', occupation='gardener') == User(name='John Doe', occupation='gardener')
1

这是输出。

在本教程中,我们介绍了 Python 中的哈希。

您可能也对以下相关教程感兴趣: Python 教程Python 列表推导或列表所有 Python 教程

Python ConfigParser教程

原文: http://zetcode.com/python/configparser/

Python ConfigParser教程显示了如何使用ConfigParser在 Python 中使用配置文件。

Python ConfigParser

ConfigParser是一个 Python 类,为 Python 程序实现基本的配置语言。 它提供类似于 Microsoft Windows INI 文件的结构。 ConfigParser允许编写可由最终用户轻松定制的 Python 程序。

配置文件由各部分组成,后跟选项的键/值对。 段名用[]字符分隔。 这些对用:=隔开。 注释以#;开头。

Python ConfigParser读取文件

在第一个示例中,我们从文件中读取配置数据。

db.ini

[mysql]
host = localhost
user = user7
passwd = s$cret
db = ydb

[postgresql]
host = localhost
user = user8
passwd = mypwd$7
db = testdb

我们有两部分配置数据。

reading_from_file.py

#!/usr/bin/env python3

import configparser

config = configparser.ConfigParser()
config.read('db.ini')

host = config['mysql']['host']
user = config['mysql']['user']
passwd = config['mysql']['passwd']
db = config['mysql']['db']

print('MySQL configuration:')

print(f'Host: {host}')
print(f'User: {user}')
print(f'Password: {passwd}')
print(f'Database: {db}')

host2 = config['postgresql']['host']
user2 = config['postgresql']['user']
passwd2 = config['postgresql']['passwd']
db2 = config['postgresql']['db']

print('PostgreSQL configuration:')

print(f'Host: {host2}')
print(f'User: {user2}')
print(f'Password: {passwd2}')
print(f'Database: {db2}')

该示例读取 MySQL 和 PostgreSQL 的配置数据。

config = configparser.ConfigParser()
config.read('db.ini')

我们启动ConfigParser并使用read()读取文件。

host = config['mysql']['host']
user = config['mysql']['user']
passwd = config['mysql']['passwd']
db = config['mysql']['db']

我们从 mysql 部分访问选项。

host2 = config['postgresql']['host']
user2 = config['postgresql']['user']
passwd2 = config['postgresql']['passwd']
db2 = config['postgresql']['db']

我们从 postgresql 部分访问选项。

$ python reading_from_file.py
MySQL configuration:
Host: localhost
User: user7
Password: s$cret
Database: ydb
PostgreSQL configuration:
Host: localhost
User: user8
Password: mypwd$7
Database: testdb

这是输出。

Python ConfigParser部分

配置数据分为几部分。 sections()读取所有部分,has_section()检查是否存在指定的部分。

sections.py

#!/usr/bin/env python3

import configparser

config = configparser.ConfigParser()
config.read('db.ini')

sections = config.sections()
print(f'Sections: {sections}')

sections.append('sqlite')

for section in sections:

    if config.has_section(section):
      print(f'Config file has section {section}')
    else:
      print(f'Config file does not have section {section}')

该示例适用于各节。

$ python sections.py
Sections: ['mysql', 'postgresql']
Config file has section mysql
Config file has section postgresql
Config file does not have section sqlite

这是输出。

Python ConfigParser从字符串读取

从 Python 3.2 开始,我们可以使用read_string()方法从字符串读取配置数据。

read_from_string.py

#!/usr/bin/env python3

import configparser

cfg_data = '''
[mysql]
host = localhost
user = user7
passwd = s$cret
db = ydb
'''

config = configparser.ConfigParser()
config.read_string(cfg_data)

host = config['mysql']['host']
user = config['mysql']['user']
passwd = config['mysql']['passwd']
db = config['mysql']['db']

print(f'Host: {host}')
print(f'User: {user}')
print(f'Password: {passwd}')
print(f'Database: {db}')

该示例从字符串读取配置。

Python ConfigParser从字典中读取

从 Python 3.2 开始,我们可以使用read_dict()方法从字典中读取配置数据。

read_from_dict.py

#!/usr/bin/env python3

import configparser

cfg_data = {
    'mysql': {'host': 'localhost', 'user': 'user7',
              'passwd': 's$cret', 'db': 'ydb'}
}

config = configparser.ConfigParser()
config.read_dict(cfg_data)

host = config['mysql']['host']
user = config['mysql']['user']
passwd = config['mysql']['passwd']
db = config['mysql']['db']

print(f'Host: {host}')
print(f'User: {user}')
print(f'Password: {passwd}')
print(f'Database: {db}')

该示例从 Python 字典读取配置。

cfg_data = {
    'mysql': {'host': 'localhost', 'user': 'user7',
                'passwd': 's$cret', 'db': 'ydb'}
}

键是部分名称,值是带有该部分中存在的键和值的字典。

Python ConfigParser写入

write()方法写入配置数据。

writing.py

#!/usr/bin/env python3

import configparser

config = configparser.ConfigParser()

config.add_section('mysql')

config['mysql']['host'] = 'localhost'
config['mysql']['user'] = 'user7'
config['mysql']['passwd'] = 's$cret'
config['mysql']['db'] = 'ydb'

with open('db3.ini', 'w') as configfile:
    config.write(configfile)

该示例将配置数据写入db3.ini文件。

config.add_section('mysql')

首先,我们用add_section()添加一个部分。

config['mysql']['host'] = 'localhost'
config['mysql']['user'] = 'user7'
config['mysql']['passwd'] = 's$cret'
config['mysql']['db'] = 'ydb'

然后我们设置选项。

with open('db3.ini', 'w') as configfile:
    config.write(configfile)

最后,我们用write()写入数据。

Python ConfigParser插值

ConfigParser允许在配置文件中使用插值。 它使用%()语法。

cfg.ini

[info]
users_dir= C:\Users
name= Jano
home_dir= %(users_dir)s\%(name)s

我们用插值法构建home_dir。 请注意,"s"字符是语法的一部分。

interpolation.py

#!/usr/bin/env python3

import configparser

config = configparser.ConfigParser()
config.read('cfg.ini')

users_dir = config['info']['users_dir']
name = config['info']['name']
home_dir = config['info']['home_dir']

print(f'Users directory: {users_dir}')
print(f'Name: {name}')
print(f'Home directory: {home_dir}')

该示例读取值并打印出来。

$ python interpolation.py
Users directory: C:\Users
Name: Jano
Home directory: C:\Users\Jano

这是输出。

在本教程中,我们使用ConfigParser处理 Python 中的配置数据。

您可能也会对以下相关教程感兴趣: Python 教程Python 哈希教程或列出所有 Python 教程

Python 语言

原文: http://zetcode.com/lang/python/python/

在 Python 编程教程的这一部分中,我们通常讨论 Python 编程语言。 我们展示了如何执行我们的第一个 Python 程序。

目标

本教程的目标是使您开始使用 Python 编程语言。 Python 是一门很棒的语言。 对于那些刚接触编程的人来说,这是一种理想的语言。 阅读完本教程后,您将有信心继续自己的学习。 您可以使用 Python 创建脚本,网站,游戏或桌面应用。 即使您不想成为程序员,Python 对于偶尔的程序员或业余爱好者来说也可能是一个很好的工具。

Python

Python logo Python 是一种通用的,动态的,面向对象的编程语言。 Python 语言的设计目的强调程序员的生产力和代码可读性。 Python 最初是由 Guido van Rossum 开发的。 它于 1991 年首次发布。Python 受 ABC,Haskell,Java,Lisp,Icon 和 Perl 编程语言的启发。 Python 是一种高级通用通用多平台解释型语言。

Python 是一种简约语言。 它最明显的特征之一是它不使用分号或括号。 Python 使用缩进代替。

目前,Python 有两个主要分支:Python 2.x 和 Python3.x。 Python 3.x 打破了与早期版本 Python 的向后兼容性。 它的创建是为了纠正该语言的某些设计缺陷并使其更简洁。 本教程介绍了 Python 3.x 版本。 今天,Python 由世界各地的一大批志愿者维护。 Python 是开源软件。

Python 支持多种编程样式。 它不会强迫程序员采用特定的示例。 它支持过程,面向对象和函数式编程。

Python 编程语言的官方网站是 python.org

Python 实现

正式地,Python 编程语言是一种规范。 Python 的三个主要实现:CPython,IronPython 和 Jython。 CPython 用 C 语言实现。 它是最广泛使用的 Python 实现。 人们谈论 Python 语言时,大多指的是 CPython。 IronPython 用 C# 实现。 它是.NET 框架的一部分。 同样,Jython 是 Java 中 Python 语言的实现。 Jython 程序被转换为 Java 字节码,并由 JVM(Java 虚拟机)执行。 在本教程中,我们将使用 CPython。

人气

Python 属于最流行的编程语言。 多项调查将 Python 列为十大语言。 一些非常受欢迎的 Python 项目包括分布式源管理工具 Mercurial,Django Web 框架,PyQt GUI 库或称为 Yum 的包管理工具。

Python 脚本

Unix 中的每个脚本都以 shebang 开头。 shebang 是脚本中的前两个字符:#!。 shebang 之后是解释器的路径,它将执行我们的脚本。 Shebangs 在 Windows 上不起作用; 但是最好将它们包括在 Windows 中,因为我们可能希望程序也可以在 Unix 上运行。

simple.py

#!/usr/bin/env python

# simple.py

print("The Python tutorial")

这是我们的第一个 Python 脚本。 该脚本会将"The Python tutorial"字符串打印到控制台。 Python 脚本具有.py扩展名。

$ which python
/usr/bin/python

我们可以使用which命令找出通往 Python 解释器的路径。

Python 脚本可以两种方式运行。

$ python first.py
The Python tutorial

Python 脚本作为解释器的参数提供。

$ chmod +x first.py 
$ ./first.py 
The Python tutorial

我们使用chmod命令使文件可执行。 该程序启动。

下一个示例显示了一个简单的 Ruby 脚本。

simple.rb

#!/usr/bin/ruby

# simple.rb

fruits = ["orange", "apple", "pear", "kiwi"]
fruits.each {|fruits| puts fruits}

注意 shebang 和通往 Ruby 解释器的路径。

$ ./ruby.rb 
orange
apple
pear
kiwi

这是 Ruby 脚本的输出。

最后,我们展示一个小的 Perl 脚本。

simple.pl

#!/usr/bin/perl

# simple.pl

$perl = "Practical Extraction and Report Language\n";

print $perl;

现在这个概念应该很清楚了。

Python 读取输入

input()函数从输入中读取一行,将其转换为字符串(将尾随换行符分隔),然后将其返回。 该函数带有一个可选参数,该参数将写入到标准输出而没有尾随换行符(如果存在)。

read_input.py

#!/usr/bin/env python

# read_input.py

name = input("Enter your name:")
print("Hello", name)

该示例显示提示并从控制台读取名称。 然后将问候语打印到控制台。

$ ./read_input.py 
Enter your name:Peter
Hello Peter

这是示例的输出。

Python 命令行参数

Python 程序可以接收命令行参数。 sys.argv包含传递给 Python 脚本的命令行参数列表。 argv[0]是脚本名称; 其余元素是传递给脚本的参数。

command_line_arguments.py

#!/usr/bin/env python

# command_line_arguments.py

import sys

print("Script name:", sys.argv[0])
print("Arguments:", end=" ")

for arg in sys.argv[1:]:
    print(arg, end=" ")

print()

该示例显示传递给脚本的命令行参数。

import sys

我们导入sys模块,该模块具有argv变量。

print("Script name:", sys.argv[0])

程序名称已打印。

for arg in sys.argv[1:]:
    print(arg, end=" ")

我们浏览存储在sys.argv中的参数列表,并将其打印到控制台。 使用end选项,我们在末尾添加新空格,而不是新行。

print()

最后,新行将打印到控制台。

$ ./command_line_arguments.py 1 2 3
Script name: ./command_line_arguments.py
Arguments: 1 2 3 

这是示例的示例输出。

在本章中,我们介绍了 Python 语言。

Python 日志教程

原文: http://zetcode.com/python/logging/

Python 日志教程展示了如何使用日志模块在 Python 中进行日志。

日志

日志是将信息写入日志文件的过程。 日志文件包含有关在操作系统,软件或通信中发生的各种事件的信息。

记录目的

完成记录是出于以下目的:

  • 信息收集
  • 故障排除
  • 产生统计数据
  • 审计
  • 性能分析

记录不仅限于识别软件开发中的错误。 它还可用于检测安全事件,监视策略违规,在出现问题时提供信息,查找应用瓶颈或生成使用情况数据。

要记录哪些事件

应记录的事件包括输入验证失败,认证和授权失败,应用错误,配置更改以及应用启动和关闭。

哪些事件不记录

不应记录的事件包括应用源代码,会话标识值,访问令牌,敏感的个人数据,密码,数据库连接字符串,加密键,银行帐户和持卡人数据。

记录最佳做法

以下是进行日志的一些最佳做法:

  • 日志应该有意义。
  • 日志应包含上下文。
  • 日志应在不同级别进行结构化和完成。
  • 日志应保持平衡; 它不应包含过多或过多的信息。
  • 记录消息应该是人类可以理解的,并且可以被机器解析。
  • 记录更复杂的应用应产生几个日志文件。
  • 日志应适应开发和生产。

日志模块

Python 日志模块定义了实现用于应用和库的灵活事件日志系统的函数和类。

日志模块组件

日志模块具有四个主要组件:记录器,处理器,过滤器和格式化程序。 记录器公开了应用代码直接使用的接口。 处理器将日志(由记录器创建)发送到适当的目的地。 过滤器提供了更细粒度的功能,用于确定要输出的日志。 格式化程序在最终输出中指定日志的布局。

Python 日志层次结构

Python 记录器形成一个层次结构。 名为main的记录器是main.new的父级。

子记录器将消息传播到与其祖先记录器关联的处理器。 因此,不必为应用中的所有记录器定义和配置处理器。 为顶级记录器配置处理器并根据需要创建子记录器就足够了。

Python 日志级别

级别用于标识事件的严重性。 有六个日志级别:

  • CRITICAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG
  • NOTSET

如果日志级别设置为WARNING,则所有WARNINGERRORCRITICAL消息都将写入日志文件或控制台。 如果将其设置为ERROR,则仅记录ERRORCRITICAL消息。

记录器的概念是有效级别。 如果未在记录器上显式设置级别,则将其父级别用作其有效级别。 如果父级没有显式设置的级别,则检查其父级,依此类推-搜索所有祖先,直到找到显式设置的级别。

使用getLogger()创建记录器时,级别设置为NOTSET。 如果未使用setLevel()显式设置日志级别,则消息将传播到记录器父级。 遍历记录器的祖先记录器链,直到找到具有NOTSET以外级别的祖先或到达根。 根记录器具有默认的WARNING级别设置。

根记录器

所有记录器都是根记录器的后代。 每个记录器将日志消息传递到其父级。 使用getLogger(name)方法创建新的记录器。 调用不带名称的函数(getLogger())将返回根记录器。

根记录器始终具有显式级别集,默认情况下为WARNING

根发信人位于层次结构的顶部,即使未配置,也始终存在。 通常,程序或库不应直接登录到根记录器。 而是应配置该程序的特定记录器。 根记录器可用于轻松打开和关闭所有库中的所有记录器。

Python 日志简单示例

logging模块具有简单的方法,可以立即使用而无需任何配置。 这可以用于简单的日志。

simple.py

#!/usr/bin/env python

import logging

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

该示例调用logging模块的五个方法。 消息将写入控制台。

$ simple.py
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message

请注意,使用了根记录器,并且只写入了三则消息。 这是因为默认情况下,仅写入具有级别警告和更高级别的消息。

Python 设置日志级别

记录级别由setLevel()设置。 它将此记录器的阈值设置为lvl。 严重性不及lvl的日志消息将被忽略。

set_level.py

#!/usr/bin/env python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.DEBUG)

logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

在示例中,我们将日志级别更改为DEBUG

logger = logging.getLogger('dev')

getLogger()返回具有指定名称的记录器。 如果名称为None,则返回根记录器。 名称可以是点分隔的字符串,用于定义日志层次结构。 例如"a""a.b""a.b.c"。 请注意,有一个隐式根名,未显示。

$ set_level.py
This is a warning message
This is an error message
This is a critical message

现在,所有消息均已写入。

Python 有效日志级别

有效日志级别是显式设置的级别或由记录器父级确定的级别。

effective_level.py

#!/usr/bin/env python

import logging

main_logger = logging.getLogger('main')
main_logger.setLevel(5)

dev_logger = logging.getLogger('main.dev')

print(main_logger.getEffectiveLevel())
print(dev_logger.getEffectiveLevel())

在示例中,我们检查了两个记录器的有效记录级别。

dev_logger = logging.getLogger('main.dev')

未设置dev_logger的电平; 然后使用其父级。

$ effective_level.py
5
5

这是输出。

Python 日志处理器

处理器是一个对象,负责将适当的日志消息(基于日志消息的严重性)调度到处理器的指定目标。

处理器像级别一样传播。 如果记录器未设置处理器,则其祖先链将搜索处理器。

handlers.py

#!/usr/bin/env python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

fileHandler = logging.FileHandler('test.log')
fileHandler.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(fileHandler)
logger.addHandler(consoleHandler)

logger.info('information message')

该示例为记录器创建两个处理器:文件处理器和控制台处理器。

fileHandler = logging.FileHandler('test.log')

FileHandler将日志发送到test.log文件。

consoleHandler = logging.StreamHandler()

StreamHandler将日志发送到流。 如果未指定流,则使用sys.stderr

logger.addHandler(fileHandler)

该处理器将通过addHandler()添加到记录器。

Python 日志格式化程序

格式化程序是一个对象,用于配置日志的最终顺序,结构和内容。 除消息字符串外,日志还包括日期和时间,日志名称和日志级别严重性。

formatter.py

#!/usr/bin/env python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(consoleHandler)

formatter = logging.Formatter('%(asctime)s  %(name)s  %(levelname)s: %(message)s')
consoleHandler.setFormatter(formatter)

logger.info('information message')

该示例创建一个控制台记录器,并将格式化程序添加到其处理器。

formatter = logging.Formatter('%(asctime)s  %(name)s  %(levelname)s: %(message)s')

格式化程序已创建。 它包括日期时间,记录器名称,记录级别名称和记录消息。

consoleHandler.setFormatter(formatter)

格式化程序通过setFormatter()设置为处理器。

$ formatter.py
2019-03-28 14:53:27,446  dev  INFO: information message

具有定义格式的消息显示在控制台中。

Python 日志basicConfig

basicConfig()配置根记录器。 它通过使用默认格式化程序创建流处理器来为日志系统进行基本配置。 如果没有为根记录器定义处理器,则debug()info()warning()error()critical()自动调用basicConfig()

basic_config.py

#!/usr/bin/env python

import logging

logging.basicConfig(filename='test.log', format='%(filename)s: %(message)s',
                    level=logging.DEBUG)

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

该示例使用basicConfig配置根记录器。

logging.basicConfig(filename='test.log', format='%(filename)s: %(message)s',
    level=logging.DEBUG)

使用filename,我们设置要写入日志消息的文件。 format确定将什么内容记录到文件中; 我们有文件名和消息。 使用level,设置记录阈值。

$ basic_config.py
$ cat test.log
basic_config.py: This is a debug message
basic_config.py: This is an info message
basic_config.py: This is a warning message
basic_config.py: This is an error message
basic_config.py: This is a critical message

运行该程序后,我们将五条消息写入test.log文件。

Python 日志文件配置

fileConfig()configparser格式文件中读取日志配置。

log.conf

[loggers]
keys=root,dev

[handlers]
keys=consoleHandler

[formatters]
keys=extend,simple

[logger_root]
level=INFO
handlers=consoleHandler

[logger_dev]
level=INFO
handlers=consoleHandler
qualname=dev
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=extend
args=(sys.stdout,)

[formatter_extend]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

[formatter_simple]
format=%(asctime)s - %(message)s

log.conf定义了记录器,处理器和格式化程序。

file_config.py

#!/usr/bin/env python

import logging
import logging.config

logging.config.fileConfig(fname='log.conf')

logger = logging.getLogger('dev')
logger.info('This is an information message')

该示例从log.conf读取日志配置文件。

$ file_config.py
2019-03-28 15:26:31,137 - dev - INFO - This is an information message

这是输出。

Python 日志变量

通过使用字符串格式记录动态数据。

log_variable.py

#!/usr/bin/env python

import logging

root = logging.getLogger()
root.setLevel(logging.INFO)

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format)

# incident happens

error_message = 'authentication failed'

root.error(f'error: {error_message}')

该示例将自定义数据写入日志消息。

2019-03-21 14:17:23,196 log_variable.py: error: authentication failed

这是日志消息。

Python 日志格式化日期时间

日期时间包含在asctime日志的日志消息中。 使用datefmt配置选项,我们可以格式化日期时间字符串。

date_time.py

#!/usr/bin/env python

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format,
                    datefmt='%Y-%m-%d %H:%M:%S')

logger.info("information message")

该示例格式化日志消息的日期时间。

log_format = '%(asctime)s %(filename)s: %(message)s'

我们将日期时间字符串包含在asctime中。

logging.basicConfig(filename="test.log", format=log_format,
                    datefmt='%Y-%m-%d %H:%M:%S')

datefmt选项格式化日期时间字符串。

2019-03-21 14:17:23,196 log_variable.py: error: authentication failed
2019-03-21 14:23:33 date_time.py: information message

注意日期时间字符串格式的不同。

Python 日志栈跟踪

栈跟踪是调用函数的栈,这些函数一直运行到引发异常时为止。 栈跟踪包含在exc_info选项中。

stack_trace.py

#!/usr/bin/env python

import logging

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format)

vals = [1, 2]

try:
    print(vals[4])

except Exception as e:
    logging.error("exception occurred", exc_info=True)

在该示例中,我们记录了尝试访问不存在的列表索引时引发的异常。

logging.error("exception occurred", exc_info=True)

通过将exc_info设置为True,栈跟踪将包含在日志中。

2019-03-21 14:56:21,313 stack_trace.py: exception occurred
Traceback (most recent call last):
  File "C:\Users\Jano\Documents\pyprogs\pylog\stack_trace.py", line 11, in <module>
    print(vals[4])
IndexError: list index out of range

栈跟踪包含在日志中。

Python 日志getLogger

getLogger()返回具有指定名称的记录器。 如果未指定名称,则返回根记录器。 在__name__中放置模块名称是一种常见的做法。

使用给定名称对该函数的所有调用均返回相同的记录器实例。 这意味着记录器实例永远不需要在应用的不同部分之间传递。

get_logger.py

#!/usr/bin/env python

import logging
import sys

main = logging.getLogger('main')
main.setLevel(logging.DEBUG)

handler = logging.FileHandler('my.log')

format = logging.Formatter('%(asctime)s  %(name)s
    %(levelname)s: %(message)s')
handler.setFormatter(format)

main.addHandler(handler)

main.info('info message')
main.critical('critical message')
main.debug('debug message')
main.warning('warning message')
main.error('error message')

该示例使用getLogger()创建一个新的记录器。 它有一个文件处理器和一个格式化程序。

main = logging.getLogger('main')
main.setLevel(logging.DEBUG)

创建了一个名为main的记录器; 我们将日志级别设置为DEBUG

handler = logging.FileHandler('my.log')

创建一个文件处理器。 消息将被写入my.log文件。

format = logging.Formatter('%(asctime)s  %(name)s
    %(levelname)s: %(message)s')
handler.setFormatter(format)

格式化程序已创建。 它包括时间,记录器名称,记录级别以及要记录的消息。 格式化程序通过setFormatter()设置为处理器。

main.addHandler(handler)

该处理器将通过addHandler()添加到记录器。

$ cat my.log
2019-03-21 14:15:45,439  main  INFO: info message
2019-03-21 14:15:45,439  main  CRITICAL: critical message
2019-03-21 14:15:45,439  main  DEBUG: debug message
2019-03-21 14:15:45,439  main  WARNING: warning message
2019-03-21 14:15:45,439  main  ERROR: error message

这些是书面的日志消息。

Python 日志 YAML 配置

日志详细信息可以在 YAML 配置文件中定义。 YAML 是一种人类可读的数据序列化语言。 它通常用于配置文件。

$ pip install pyyaml

我们需要安装pyyaml模块。

config.yaml

version: 1

formatters:
  simple:
    format: "%(asctime)s %(name)s: %(message)s"
  extended:
    format: "%(asctime)s %(name)s %(levelname)s: %(message)s"

handlers:
  console:
    class: logging.StreamHandler
    level: INFO
    formatter: simple

  file_handler:
    class: logging.FileHandler
    level: INFO
    filename: test.log
    formatter: extended
    propagate: false

loggers:
  dev:
    handlers: [console, file_handler]
  test:
    handlers: [file_handler]
root:
  handlers: [file_handler]

在配置文件中,我们定义了各种格式化程序,处理器和记录器。 propagate选项可防止将日志消息传播到父级记录器。 就我们而言,是根记录器。 否则,消息将被复制。

log_yaml.py

#!/usr/bin/env python

import logging
import logging.config
import yaml

with open('config.yaml', 'r') as f:

    log_cfg = yaml.safe_load(f.read())

logging.config.dictConfig(log_cfg)

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

logger.info('This is an info message')
logger.error('This is an error message')

在示例中,我们读取配置文件并使用dev记录器。

$ log_yaml.py
2019-03-28 11:36:54,854 dev: This is an info message
2019-03-28 11:36:54,855 dev: This is an error message

当我们运行程序时,控制台上有两条消息。 控制台处理器使用带有较少信息的简单格式化程序。

...
2019-03-28 11:36:54,854 dev INFO: This is an info message
2019-03-28 11:36:54,855 dev ERROR: This is an error message

test.log文件中有日志消息。 它们由扩展的格式化程序提供,具有更多信息。

在本教程中,我们使用了 Python 日志库。 您可能也对相关教程感兴趣: Python Jinja 教程Bottle 教程Python 教程或列表 Python 教程

Python argparse教程

原文: http://zetcode.com/python/argparse/

Python argparse教程展示了如何使用argparse模块解析 Python 中的命令行参数。

Python 的argparse

通过argparse模块,可以轻松编写用户友好的命令行界面。 它解析sys.argv中定义的参数。

argparse模块还会自动生成帮助和使用消息,并在用户为程序提供无效参数时发出错误。

argparse是标准模块; 我们不需要安装它。

使用ArgumentParser创建一个解析器,并使用add_argument()添加一个新参数。 参数可以是可选的,必需的或定位的。

Python argparse可选参数

下面的示例创建一个简单的参数解析器。

optional_arg.py

#!/usr/bin/env python

import argparse

# help flag provides flag help
# store_true actions stores argument as True

parser = argparse.ArgumentParser()

parser.add_argument('-o', '--output', action='store_true', 
    help="shows output")

args = parser.parse_args()

if args.output:
    print("This is some output")

该示例添加了一个具有两个选项的参数:短的-o和长的--ouput。 这些是可选参数。

import argparse

该模块已导入。

parser.add_argument('-o', '--output', action='store_true', 
    help="shows output")

参数添加了add_argument()。 如果设置为store_true,则action会将参数存储为True。 help 选项提供参数帮助。

args = parser.parse_args()

参数由parse_args()解析。 解析的参数作为对象属性存在。 在我们的例子中,将有args.output属性。

if args.output:
    print("This is some output")

如果存在该参数,我们将显示一些输出。

$ optional_arg.py -o
This is some output
$ optional_arg.py --output
This is some output

我们使用-o--output运行程序。

$ optional_arg.py --help
usage: optional_arg.py [-h] [-o]

optional arguments:
    -h, --help    show this help message and exit
    -o, --output  shows output

我们可以向程序显示帮助。

Python argparse必需参数

使用required选项需要一个参数。

required_arg.py

#!/usr/bin/env python

import argparse

# required arg

parser = argparse.ArgumentParser()

parser.add_argument('--name', required=True)

args = parser.parse_args()

print(f'Hello {args.name}')

该示例必须指定name选项; 否则失败。

$ required_arg.py --name Peter
Hello Peter

$ required_arg.py
usage: required_arg.py [-h] --name NAME
required_arg.py: error: the following arguments are required: --name

这是输出。

Python argparse位置参数

以下示例适用于位置参数。 它们是使用add_argument()创建的。

positional_arg.py

#!/usr/bin/env python

import argparse

# positional args

parser = argparse.ArgumentParser()

parser.add_argument('name')
parser.add_argument('age')

args = parser.parse_args()

print(f'{args.name} is {args.age} years old')

该示例需要两个位置参数:name 和 age。

parser.add_argument('name')
parser.add_argument('age')

创建位置参数时不带破折号前缀字符。

$ positional_arg.py Peter 23
Peter is 23 years old

这是示例输出。

Python argparse目标

add_argument()dest选项为参数指定名称。 如果未给出,则从选项中推断出来。

dest.py

#!/usr/bin/env python

import argparse
import datetime

# dest gives a different name to a flag

parser = argparse.ArgumentParser()

parser.add_argument('-n', dest='now', action='store_true', help="shows now")

args = parser.parse_args()

# we can refer to the flag
# by a new name
if args.now:

    now = datetime.datetime.now()
    print(f"Now: {now}")

程序将now名称赋予-n参数。

$ dest.py -n
Now: 2019-03-22 17:37:40.406571

这是输出。

Python argparse类型

type参数确定参数类型。

rand_int.py

#!/usr/bin/env python

import argparse
import random

# type determines the type of the argument

parser = argparse.ArgumentParser()

parser.add_argument('-n', type=int, required=True, 
    help="define the number of random integers")
args = parser.parse_args()

n = args.n

for i in range(n):
    print(random.randint(-100, 100))

程序显示从-100 到 100 的 n 个随机整数。

parser.add_argument('-n', type=int, required=True, 
    help="define the number of random integers")

-n选项需要整数值,这是必需的。

$ rand_int.py -n 3
92
-61
-61

这是一个示例输出。

Python argparse默认

如果未指定default选项,则指定默认值。

power.py

#!/usr/bin/env python

import argparse

# required defines a mandatory argument 
# default defines a default value if not specified

parser = argparse.ArgumentParser()

parser.add_argument('-b', type=int, required=True, help="defines the base value")
parser.add_argument('-e', type=int, default=2, help="defines the exponent value")
args = parser.parse_args()

val = 1

base = args.b
exp = args.e

for i in range(exp):
    val *= base

print(val)

该示例计算指数。 不需要指数值; 如果未给出,则默认值为 2。

$ power.py -b 3
9
$ power.py -b 3 -e 3
27

这是输出。

Python argparse metavar

metavar选项为错误的期望值命名,并提供帮助输出。

metavar.py

#!/usr/bin/env python

import argparse

# metavar gives name to the expected value 
# in error and help outputs

parser = argparse.ArgumentParser()

parser.add_argument('-v', type=int, required=True, metavar='value', 
    help="computes cube for the given value")
args = parser.parse_args()

print(args)

val = args.v

print(val * val * val)

该示例将期望值命名为value。 默认名称为V

$ metavar.py -h
usage: metavar.py [-h] -v value

optional arguments:
  -h, --help  show this help message and exit
  -v value    computes cube for the given value

给定的名称显示在帮助输出中。

Python argparse附加操作

append操作允许对重复选项进行分组。

appending.py

#!/usr/bin/env python

import argparse

# append action allows to group repeating
# options

parser = argparse.ArgumentParser()

parser.add_argument('-n', '--name', dest='names', action='append', 
    help="provides names to greet")

args = parser.parse_args()

names = args.names

for name in names:
    print(f'Hello {name}!')

该示例生成问候语,并使用nname选项指定所有名称。 它们可以重复多次重复。

$ appending.py -n Peter -n Lucy --name Jane
Hello Peter!
Hello Lucy!
Hello Jane!

这是输出。

Python 的argparse nargs

nargs指定应使用的命令行参数的数量。

charseq.py

#!/usr/bin/env python

import argparse
import sys

# nargs sets the required number of argument values
# metavar gives name to argument values in error and help output

parser = argparse.ArgumentParser()
parser.add_argument('chars', type=str, nargs=2, metavar='c',
                    help='starting and ending character')

args = parser.parse_args()

try:
    v1 = ord(args.chars[0])
    v2 = ord(args.chars[1])

except TypeError as e:

    print('Error: arguments must be characters')
    parser.print_help()
    sys.exit(1)

if v1 > v2:
    print('first letter must precede the second in alphabet')
    parser.print_help()
    sys.exit(1)

该示例显示了从字符一到字符二的字符序列。 它需要两个参数。

parser.add_argument('chars', type=str, nargs=2, metavar='c',
    help='starting and ending character')

使用nargs=2,我们指定期望两个参数。

$ charseq.py e k
e f g h i j k

程序显示从ek的字符序列。

可以使用*字符设置可变数量的参数。

var_args.py

#!/usr/bin/env python

import argparse

# * nargs expects 0 or more arguments

parser = argparse.ArgumentParser()
parser.add_argument('num', type=int, nargs='*')
args = parser.parse_args()

print(f"The sum of values is {sum(args.num)}")

该示例计算值的总和。 我们可以为程序指定可变数量的参数。

$ var_args.py 1 2 3 4 5
The sum of values is 15

这是输出。

Python argparse选择

choices选项将参数限制为给定列表。

mytime.py

#!/usr/bin/env python

import argparse
import datetime
import time

# choices limits argument values to the 
# given list

parser = argparse.ArgumentParser()

parser.add_argument('--now', dest='format', choices=['std', 'iso', 'unix', 'tz'],
                    help="shows datetime in given format")

args = parser.parse_args()
fmt = args.format

if fmt == 'std':
    print(datetime.date.today())
elif fmt == 'iso':
    print(datetime.datetime.now().isoformat())
elif fmt == 'unix':
    print(time.time())
elif fmt == 'tz':
    print(datetime.datetime.now(datetime.timezone.utc))

在示例中,now选项可以接受以下值:stdisounixtz

$ mytime.py --now iso
2019-03-27T11:34:54.106643

$ mytime.py --now unix
1553682898.422863

这是一个示例输出。

头例子

以下示例模仿 Linux head命令。 它显示了文件开头的 n 行文本。

words.txt

sky
top
forest
wood
lake
wood

对于示例,我们有这个小的测试文件。

head.py

#!/usr/bin/env python

import argparse
from pathlib import Path

# head command
# working with positional arguments

parser = argparse.ArgumentParser()

parser.add_argument('f', type=str, help='file name')
parser.add_argument('n', type=int, help='show n lines from the top')

args = parser.parse_args()

filename = args.f

lines = Path(filename).read_text().splitlines()

for line in lines[:args.n]:
    print(line) 

该示例有两个选项:f表示文件名,-n表示要显示的行数。

$ head.py words.txt 3
sky
top
forest

这是输出。

这是 Python argparse教程。 您可能也对 Python pathlib教程Python 教程感兴趣。

Python SQLite 教程

原文: http://zetcode.com/python/sqlite/

这是用于 SQLite 数据库的 Python 编程教程。 它涵盖了使用 Python 语言进行 SQLite 编程的基础。 ZetCode 拥有用于 Python SQLite 的完整电子书: Python SQLite 电子书

要使用本教程,我们必须在系统上安装 Python 语言,SQLite 数据库,pysqlite语言绑定和sqlite3命令行工具。

为了使用 SQLite 数据库,我们可以安装sqlite3或 SQLite 浏览器 GUI。

$ python
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> sqlite3.version
'2.6.0'
>>> sqlite3.sqlite_version
'3.21.0'

在外壳程序中,我们启动 Python 交互式解释器。 我们可以看到 Python 版本。 在我们的例子中是 Python 3.7.0。 sqlite.versionpysqlite(2.6.0)的版本,它是 Python 语言与 SQLite 数据库的绑定。 sqlite3.sqlite_version为我们提供了 SQLite 数据库库的版本。 在我们的例子中,版本是 3.21.0。

SQLite

SQLite 是嵌入式关系数据库引擎。 该文档称其为自包含,无服务器,零配置和事务型 SQL 数据库引擎。 如今,它在全球使用着数亿册,非常受欢迎。 几种编程语言都内置了对 SQLite 的支持,包括 Python 和 PHP。

创建 SQLite 数据库

现在,我们将使用sqlite3命令行工具创建一个新数据库。

$ sqlite3 ydb.db
SQLite version 3.27.2 2019-02-25 16:06:06
Enter ".help" for usage hints.
sqlite>

我们为sqlite3 tool提供了一个参数; ydb.db是数据库名称。 这是我们磁盘上的文件。 如果存在,则将其打开。 如果不是,则创建它。

sqlite> .tables
sqlite> .exit
$ ls
ydb.db

.tables命令提供ydb.db数据库中的表的列表。 当前没有表格。 .exit命令终止sqlite3命令行工具的交互式会话。 ls Unix 命令显示当前工作目录的内容。 我们可以看到ydb.db文件。 所有数据将存储在该单个文件中。

Python SQLite 版本示例

在第一个代码示例中,我们将获得 SQLite 数据库的版本。

version.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

con = None

try:
    con = sqlite.connect('ydb.db')

    cur = con.cursor()
    cur.execute('SELECT SQLITE_VERSION()')

    data = cur.fetchone()[0]

    print(f"SQLite version: {data}")

except sqlite.Error as e:

    print(f"Error {e.args[0]}")
    sys.exit(1)

finally:

    if con:
        con.close()

在上面的 Python 脚本中,我们连接到先前创建的ydb.db数据库。 我们执行一条 SQL 语句,该语句返回 SQLite 数据库的版本。

import sqlite3 as sqlite

我们导入sqlite3并为其命名。

con = None

我们将con变量初始化为None。 如果无法创建与数据库的连接(例如磁盘已满),则不会定义连接变量。 这将导致finally子句中的错误。

con = sqlite.connect('ydb.db')

我们连接到ydb.db数据库。 connect()方法返回一个连接对象。

cur = con.cursor()
cur.execute('SELECT SQLITE_VERSION()')

从连接中,我们得到游标对象。 游标用于遍历结果集中的记录。 我们调用游标的execute()方法并执行 SQL 语句。

data = cur.fetchone()[0]

我们获取数据。 由于只检索一条记录,因此我们称为fetchone()方法。

print(f"SQLite version: {data}")

我们将检索到的数据打印到控制台。

except sqlite.Error as e:

    print(f"Error {e.args[0]}")
    sys.exit(1)

如果发生异常,我们将输出一条错误消息,并以错误代码 1 退出脚本。

finally:

    if con:
        con.close()

在最后一步,我们释放资源。

在第二个示例中,我们再次获得 SQLite 数据库的版本。 这次我们将使用with关键字。

version2.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute('SELECT SQLITE_VERSION()')

    data = cur.fetchone()[0]

    print(f"SQLite version: {data}")

该脚本返回 SQLite 数据库的当前版本。 通过使用with关键字。 代码更紧凑。

with con:

使用with关键字,Python 解释器会自动释放资源。 它还提供错误处理。

$ ./version.py
SQLite version: 3.21.0

这是输出。

Python SQLite execute

我们创建一个cars表并在其中插入几行。 我们使用execute()

create_table.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()

    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.execute("INSERT INTO cars VALUES(1,'Audi',52642)")
    cur.execute("INSERT INTO cars VALUES(2,'Mercedes',57127)")
    cur.execute("INSERT INTO cars VALUES(3,'Skoda',9000)")
    cur.execute("INSERT INTO cars VALUES(4,'Volvo',29000)")
    cur.execute("INSERT INTO cars VALUES(5,'Bentley',350000)")
    cur.execute("INSERT INTO cars VALUES(6,'Citroen',21000)")
    cur.execute("INSERT INTO cars VALUES(7,'Hummer',41400)")
    cur.execute("INSERT INTO cars VALUES(8,'Volkswagen',21600)")

上面的脚本创建一个cars表,并将 8 行插入到该表中。

cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")

该 SQL 语句创建一个新的cars表。 该表有三列。

cur.execute("INSERT INTO cars VALUES(1,'Audi',52642)")
cur.execute("INSERT INTO cars VALUES(2,'Mercedes',57127)")

这两行将两辆车插入表。 使用with关键字,更改将自动提交。 否则,我们将不得不手动提交它们。

sqlite> .mode column
sqlite> .headers on

我们使用sqlite3工具验证写入的数据。 首先,我们修改数据在控制台中的显示方式。 我们使用列模式并打开标题。

sqlite>  select * from cars;
id          name        price
----------  ----------  ----------
1           Audi        52642
2           Mercedes    57127
3           Skoda       9000
4           Volvo       29000
5           Bentley     350000
6           Citroen     21000
7           Hummer      41400
8           Volkswagen  21600

这是我们已写入cars表的数据。

Python SQLite executemany

我们将创建相同的表。 这次使用便捷的executemany()方法。

create_table2.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

cars = (
    (1, 'Audi', 52642),
    (2, 'Mercedes', 57127),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Hummer', 41400),
    (7, 'Volkswagen', 21600)
)

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)

程序将删除cars表(如果存在)并重新创建它。

cur.execute("DROP TABLE IF EXISTS cars")
cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")

如果存在,则第一个 SQL 语句将删除cars表。 第二条 SQL 语句创建cars表。

cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)

我们使用便捷的executemany()方法将 8 行插入到表中。 此方法的第一个参数是参数化的 SQL 语句。 第二个参数是数据,以元组的元组的形式。

Python SQLite executescript

我们提供了另一种使用executescript()创建cars表的方法。 我们手动提交更改并提供我们自己的错误处理。

create_table3.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

con = None

try:
    con = sqlite.connect('ydb.db')

    cur = con.cursor()

    cur.executescript("""
        DROP TABLE IF EXISTS cars;
        CREATE TABLE cars(id INT, name TEXT, price INT);
        INSERT INTO cars VALUES(1,'Audi',52642);
        INSERT INTO cars VALUES(2,'Mercedes',57127);
        INSERT INTO cars VALUES(3,'Skoda',9000);
        INSERT INTO cars VALUES(4,'Volvo',29000);
        INSERT INTO cars VALUES(5,'Bentley',350000);
        INSERT INTO cars VALUES(6,'Citroen',21000);
        INSERT INTO cars VALUES(7,'Hummer',41400);
        INSERT INTO cars VALUES(8,'Volkswagen',21600);
        """)

    con.commit()

except sqlite.Error as e:

    if con:
        con.rollback()

    print(f"Error {e.args[0]}")
    sys.exit(1)

finally:

    if con:
        con.close()

在上面的脚本中,我们使用executescript()方法(重新)创建cars表。

cur.executescript("""
    DROP TABLE IF EXISTS cars;
    CREATE TABLE cars(id INT, name TEXT, price INT);
    INSERT INTO cars VALUES(1,'Audi',52642);
    INSERT INTO cars VALUES(2,'Mercedes',57127);
...

executescript()方法允许我们一步执行整个 SQL 代码。

con.commit()

如果没有with关键字,则必须使用commit()方法来提交更改。

except sqlite.Error as e:

    if con:
        con.rollback()

    print(f"Error {e.args[0]}")
    sys.exit(1)

发生错误时,所做的更改将回滚,并在终端上显示一条错误消息。

Python SQLite lastrowid

有时,我们需要确定最后插入的行的 ID。 在 Python SQLite 中,我们使用游标对象的lastrowid属性。

lastrowid.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect(':memory:')

with con:

    cur = con.cursor()
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT);")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom');")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca');")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim');")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert');")

    last_row_id = cur.lastrowid

    print(f"The last Id of the inserted row is {last_row_id}")

我们在内存中创建一个friends表。 ID 会自动递增。

cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT);")

在 SQLite 中,INTEGER PRIMARY KEY列自动增加。 还有一个AUTOINCREMENT关键字。 在INTEGER PRIMARY KEY AUTOINCREMENT中使用时,会使用稍微不同的 ID 创建算法。

cur.execute("INSERT INTO friends(name) VALUES ('Tom');")
cur.execute("INSERT INTO friends(name) VALUES ('Rebecca');")
cur.execute("INSERT INTO friends(name) VALUES ('Jim');")
cur.execute("INSERT INTO friends(name) VALUES ('Robert');")

使用自动增量时,我们必须明确声明列名,而忽略自动增量的列名。 这四个语句在friends表中插入四行。

last_row_id = cur.lastrowid

使用lastrowid获得最后插入的行 ID。

$ ./lastrowid.py
The last Id of the inserted row is 4

我们看到了程序的输出。

Python SQLite 使用fetchall检索数据

fetchall()方法获取查询结果集的所有(或所有剩余)行,并返回一个元组列表。

fetch_all.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print(f"{row[0]} {row[1]} {row[2]}")

在此示例中,我们从cars表中检索所有数据。

cur.execute("SELECT * FROM cars")

该 SQL 语句从cars表中选择所有数据。

rows = cur.fetchall()

fetchall()方法获取所有记录。 它返回一个结果集。 从技术上讲,它是一个元组的元组。 每个内部元组代表表中的一行。

for row in rows:
    print(f"{row[0]} {row[1]} {row[2]}")

我们将数据逐行打印到控制台。

$ ./fetch_all.py
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

这是示例的输出。

Python SQLite fetchone

fetchone()返回查询结果集的下一行,返回单个元组,或者在没有更多数据可用时返回None

fetch_one.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    while True:

        row = cur.fetchone()

        if row == None:
            break

        print(f"{row[0]} {row[1]} {row[2]}")

在此脚本中,我们连接到数据库,并逐个读取cars表的行。

while True:

我们从while循环访问数据。 当我们读取最后一行时,循环终止。

row = cur.fetchone()

if row == None:
    break

fetchone()方法返回表的下一行。 如果没有剩余数据,则返回None。 在这种情况下,我们打破了循环。

print(f"{row[0]} {row[1]} {row[2]}")

数据以元组的形式返回。 在这里,我们从元组中选择记录。 第一个是 ID,第二个是汽车名称,第三个是汽车的价格。

$ ./fetch_one.py
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

这是示例的输出。

Python SQLite 字典游标

默认游标以元组的元组返回数据。 当我们使用字典游标时,数据以 Python 字典的形式发送。 这样,我们可以通过列名称来引用数据。

dictionary_cursor.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    con.row_factory = sqlite.Row

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print(f"{row['id']} {row['name']} {row['price']}")

在此示例中,我们使用字典游标打印cars表的内容。

con.row_factory = sqlite.Row

我们选择一个字典游标。 现在,我们可以按列名访问记录。

for row in rows:
    print(f"{row['id']} {row['name']} {row['price']}")

通过列名访问数据。

Python SQLite 参数化语句

现在,我们将关注参数化查询。 当使用参数化查询时,我们使用占位符,而不是直接将值写入语句。 参数化查询可提高安全性和性能。

Python sqlite3模块支持两种类型的占位符:问号和命名占位符。

带问号的参数化语句

在第一个示例中,我们使用问号的语法。

parameterized_query.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

uId = 1
uPrice = 62300

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute("UPDATE cars SET price=? WHERE id=?", (uPrice, uId))

    print(f"Number of rows updated: {cur.rowcount}")

我们更新了一辆车的价格。 在此代码示例中,我们使用问号占位符。

cur.execute("UPDATE cars SET price=? WHERE id=?", (uPrice, uId))

问号?是值的占位符。 这些值将添加到占位符。

print(f"Number of rows updated: {cur.rowcount}")

rowcount属性返回更新的行数。 在我们的情况下,一行已更新。

具有命名占位符的参数化语句

第二个示例使用带有命名占位符的参数化语句。

named_placeholders.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

uId = 4

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT name, price FROM cars WHERE Id=:Id", {"Id": uId})

    row = cur.fetchone()
    print(f"{row[0]}, {row[1]}")

我们使用命名的占位符选择汽车的名称和价格。

cur.execute("SELECT name, price FROM cars WHERE Id=:Id", {"Id": uId})

命名的占位符以冒号开头。

Python SQLite 插入图片

在本节中,我们将图像插入到 SQLite 数据库中。 请注意,有些人反对将图像放入数据库。 在这里,我们只展示如何做。 我们不讨论是否将图像保存在数据库中的技术问题。

sqlite> CREATE TABLE images(id INTEGER PRIMARY KEY, data BLOB);

对于此示例,我们创建一个名为Images的新表。 对于图像,我们使用BLOB数据类型,表示二进制大型对象。

insert_image.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

def readImage():

    fin = None

    try:
        fin = open("sid.jpg", "rb")
        img = fin.read()
        return img

    except IOError as e:

        print(e)
        sys.exit(1)

    finally:

        if fin:
            fin.close()

con = None

try:
    con = sqlite.connect('ydb.db')

    cur = con.cursor()

    data = readImage()
    binary = sqlite.Binary(data)
    cur.execute("INSERT INTO images(data) VALUES (?)", (binary,) )

    con.commit()

except sqlite.Error as e:

    if con:
        con.rollback()

    print(e)
    sys.exit(1)

finally:

    if con:
        con.close()

在此脚本中,我们从当前工作目录中读取图像,并将其写入 SQLite ydb.db数据库的images表中。

try:
    fin = open("sid.png", "rb")
    img = fin.read()
    return img

我们从文件系统读取二进制数据。 我们有一个名为sid.png的 JPG 图像。

binary = sqlite.Binary(data)

使用 SQLite Binary对象对数据进行编码。

cur.execute("INSERT INTO images(data) VALUES (?)", (binary,) )

该 SQL 语句用于将映像插入数据库。

Python SQLite 读取图像

在本节中,我们将执行相反的操作:我们从数据库表中读取一个图像。

read_image.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

def writeImage(data):

    fout = None

    try:
        fout = open('sid2.png','wb')
        fout.write(data)

    except IOError as e:

        print(e)
        sys.exit(1)

    finally:

        if fout:
            fout.close()

con = None

try:
    con = sqlite.connect('ydb.db')

    cur = con.cursor()
    cur.execute("SELECT data FROM images LIMIT 1")
    data = cur.fetchone()[0]

    writeImage(data)

except sqlite.Error as e:

    print(e)
    sys.exit(1)

finally:

    if con:
        con.close()

我们从Images表中读取图像数据,并将其写入另一个文件woman2.jpg中。

try:
    fout = open('sid2.png','wb')
    fout.write(data)

我们以写入模式打开一个二进制文件。 来自数据库的数据被写入文件。

cur.execute("SELECT data FROM images LIMIT 1")
data = cur.fetchone()[0]

这两行从images表中选择并获取数据。 我们从第一行获取二进制数据。

Python SQLite 元数据

元数据是有关数据库中数据的信息。 SQLite 中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用PRAGMA命令获取 SQLite 中的元数据。 SQLite 对象可能具有属性,即元数据。 最后,我们还可以通过查询 SQLite 系统sqlite_master表来获取特定的元数据。

column_names.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()

    cur.execute('PRAGMA table_info(cars)')

    data = cur.fetchall()

    for d in data:
        print(f"{d[0]} {d[1]} {d[2]}")

在此示例中,我们发出PRAGMA table_info(tableName)命令,以获取有关cars表的一些元数据信息。

cur.execute('PRAGMA table_info(cars)')

PRAGMA table_info(tableName)命令为cars表中的每一列返回一行。 结果集中的列包括列顺序号,列名称,数据类型,该列是否可以为NULL以及该列的默认值。

for d in data:
    print(f"{d[0]} {d[1]} {d[2]}")

根据提供的信息,我们打印列顺序号,列名称和列数据类型。

$ ./column_names.py
0 id INT
1 name TEXT
2 price INT

这是示例的输出。

在下面的示例中,我们打印cars表中的所有行及其列名。

column_names2.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute('SELECT * FROM cars')

    col_names = [cn[0] for cn in cur.description]

    rows = cur.fetchall()

    print(f"{col_names[0]:3} {col_names[1]:10} {col_names[2]:7}")

    for row in rows:
        print(f"{row[0]:<3} {row[1]:<10} {row[2]:7}")

我们将cars表的内容打印到控制台。 现在,我们也包括列的名称。 记录与列名对齐。

col_names = [cn[0] for cn in cur.description]

我们从游标对象的description属性获得列名。

print(f"{col_names[0]:3} {col_names[1]:10} {col_names[2]:7}")

此行打印cars表的三个列名。

for row in rows:
    print(f"{row[0]:<3} {row[1]:<10} {row[2]:7}")

我们使用for循环打印行。 数据与列名对齐。

$  ./column_names2.py 
id  name       price  
1   Audi         62300
2   Mercedes     57127
3   Skoda         9000
4   Volvo        29000
5   Bentley     350000
6   Hummer       41400
7   Volkswagen   21600

这是输出。

在与元数据有关的最后一个示例中,我们将列出ydb.db数据库中的所有表。

list_tables.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

con = sqlite.connect('ydb.db')

with con:

    cur = con.cursor()
    cur.execute("SELECT name FROM sqlite_master WHERE type='table'")

    rows = cur.fetchall()

    for row in rows:
        print(row[0])

该代码示例将当前数据库中的所有可用表打印到终端。

cur.execute("SELECT name FROM sqlite_master WHERE type='table'")

表名存储在系统sqlite_master表中。

$ ./list_tables.py
cars
images

这些是我们系统上的表。

Python SQLite 数据导出

我们可以转储 SQL 格式的数据以创建数据库表的简单备份。

export_table.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

cars = (
    (1, 'Audi', 52643),
    (2, 'Mercedes', 57642),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Hummer', 41400),
    (7, 'Volkswagen', 21600)
)

def writeData(data):

    f = open('cars.sql', 'w')

    with f:
        f.write(data)

con = sqlite.connect(':memory:')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
    cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)
    cur.execute("DELETE FROM cars WHERE price < 30000")

    data = '\n'.join(con.iterdump())

    writeData(data)

在上面的示例中,我们在内存中重新创建了cars表。 我们从表中删除一些行,并将表的当前状态转储到cars.sql文件中。 该文件可以用作表的当前备份。

def writeData(data):

    f = open('cars.sql', 'w')

    with f:
        f.write(data)

表中的数据正在写入文件。

con = sqlite.connect(':memory:')

我们在内存中创建一个临时表。

cur.execute("DROP TABLE IF EXISTS cars")
cur.execute("CREATE TABLE cars(id INT, name TEXT, price INT)")
cur.executemany("INSERT INTO cars VALUES(?, ?, ?)", cars)
cur.execute("DELETE FROM cars WHERE price < 30000")

这些行创建cars表,插入值并删除行,其中price小于 30000 单位。

data = '\n'.join(con.iterdump())

con.iterdump()返回一个迭代器,以 SQL 文本格式转储数据库。 内置的join()函数采用迭代器,并将迭代器中的所有字符串连接在一起,并用新行分隔。 此数据将写入writeData()函数中的cars.sql文件中。

$ cat cars.sql
BEGIN TRANSACTION;
CREATE TABLE cars(id INT, name TEXT, price INT);
INSERT INTO "cars" VALUES(1,'Audi',52643);
INSERT INTO "cars" VALUES(2,'Mercedes',57642);
INSERT INTO "cars" VALUES(5,'Bentley',350000);
INSERT INTO "cars" VALUES(6,'Hummer',41400);
COMMIT;

废弃的内存车表中的内容。

Python SQLite 导入数据

现在,我们将执行反向操作。 我们将转储的表导入回内存。

import_table.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite

def readData():

    f = open('cars.sql', 'r')

    with f:

        data = f.read()

        return data

con = sqlite.connect(':memory:')

with con:

    cur = con.cursor()

    sql = readData()
    cur.executescript(sql)

    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print(row)

在此脚本中,我们读取cars.sql文件的内容并执行它。 这将重新创建转储的表。

def readData():

    f = open('cars.sql', 'r')

    with f:

        data = f.read()

        return data

readData()方法内部,我们读取cars.sql表的内容。

cur.executescript(sql)

我们调用executescript()方法来启动 SQL 脚本。

cur.execute("SELECT * FROM cars")

rows = cur.fetchall()

for row in rows:
    print(row)

我们验证数据。

$ ./import_table.py
(1, u'Audi', 52643)
(2, u'Mercedes', 57642)
(5, u'Bentley', 350000)
(6, u'Hummer', 41400)

输出显示我们已经成功地重新创建了保存的汽车表。

Python SQLite 事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

默认情况下,Python sqlite3模块在数据修改语言(DML)语句(即INSERT/UPDATE/DELETE/REPLACE)之前隐式发出BEGIN语句。

sqlite3用于在 DDL 语句之前隐式提交一个打开的事务。 这已不再是这种情况。

手动事务以BEGIN TRANSACTION语句开始,并以COMMITROLLBACK语句结束。

SQLite 支持三种非标准事务级别:DEFERREDIMMEDIATEEXCLUSIVE。 Python SQLite 模块还支持自动提交模式,该模式下对表的所有更改均立即生效。

no_commit.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

con = None

try:
    con = sqlite.connect('ydb.db')

    cur = con.cursor()
    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT)")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

    #con.commit()

except sqlite.Error as e:

    if con:
        con.rollback()

    print(e)
    sys.exit(1)

finally:

    if con:
        con.close()

我们创建一个friends表,并尝试用数据填充它。 但是,正如我们将看到的,数据未提交。

#con.commit()

注释commit()方法。 如果我们取消注释该行,则数据将被写入表中。

sqlite> .tables
cars     friends  images 
sqlite> SELECT Count(id) FROM friends;
Count(id) 
----------
0         
sqlite>

表已创建,但数据未写入表中。

Python SQLite 自动提交

在自动提交模式下,将立即执行一条 SQL 语句。

autocommit.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sqlite3 as sqlite
import sys

con = None

try:
    con = sqlite.connect('ydb.db', isolation_level=None)

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id INTEGER PRIMARY KEY, name TEXT)")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

except sqlite.Error as e:

    print(e)
    sys.exit(1)

finally:

    if con:
        con.close()

在此示例中,我们以自动提交模式连接到数据库。

con = sqlite.connect('ydb.db', isolation_level=None)

当将isolation_level设置为None时,我们具有自动提交模式。

$ ./autocommit.py

sqlite> SELECT * FROM friends;
Id          Name
----------  ----------
1           Tom
2           Rebecca
3           Jim
4           Robert

数据已成功提交到friends表。

这是 Python SQLite 教程。

您可能还想查看 ZetCode 上的 Python 教程PyMySQL 教程Python PostgreSQL 教程

Python Cerberus 教程

原文: http://zetcode.com/python/cerberus/

Python Cerberus 教程展示了如何在 Python 中使用 Cerberus 验证数据。

Cerberus

Cerberus 是一个 Python 验证库,它提供了功能强大而又简单轻巧的数据验证功能。 它被设计为易于扩展,允许自定义验证。

Cerberus 通过定义数据验证模式来工作。 该模式将传递到Validator并使用validate()进行验证。 它具有适用于数据的一组规则,例如requiredminmax

可以将多个规则应用于数据字段。 如果验证失败,我们可以获得带有errors属性的错误消息。

Cerberus 类型

以下是 Cerberus 的简单演示。 使用type规则,我们可以设置字段的预期数据类型。

simple.py

#!/usr/bin/env python3

from cerberus import Validator

schema = {'name': {'type': 'string'}}
v = Validator(schema)

document = {'name': 'john doe'}

if v.validate(document):
    print('data is valid')
else:
    print('invalid data')

在示例中,我们验证name字段; 我们希望它是一个字符串值。

from cerberus import Validator

我们导入Validator类。

schema = {'name': {'type': 'string'}}

我们定义模式。 这是 Python 字典。 我们指定名称字段必须为字符串。

document = {'name': 'john doe'}

这是我们的数据。

if v.validate(document):
    print('data is valid')
else:
    print('invalid data')

我们使用validate()验证数据。

$ ./simple.py
data is valid

这是输出。

在第二个示例中,我们检查字符串和列表类型。

types.py

#!/usr/bin/env python3

from cerberus import Validator

v = Validator()
v.schema = {'words': {'type': ['string', 'list']}}

if v.validate({'words': 'falcon'}):
    print('valid data')
else:
    print('invalid data')

if v.validate({'words': ['falcon', 'sky', 'cloud']}):
    print('valid data')
else:
    print('invalid data')

该示例验证words字段是字符串还是列表。

Cerberus 法则

required规则使该字段为必填字段。

required.py

#!/usr/bin/env python3

from cerberus import Validator

v = Validator()
v.schema = {'name': {'required': True, 'type': 'string'},
            'age': {'type': 'integer'}}

if v.validate({'age': 34}):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

该示例有两个数据字段:nameagename是必填字段。

$ ./required.py
invalid data
{'name': ['required field']}

我们省略了name字段; 因此,验证失败。

Cerberus 最小和最大规则

minmax规则设置整数,浮点数和数字类型所允许的最小值和最大值。

对于字符串类型,我们可以使用minlengthmaxlength

min_max.py

#!/usr/bin/env python3

from cerberus import Validator

v = Validator()
v.schema = {'name': { 'type': 'string', 'minlength': 2},
    'age': {'type': 'integer', 'min': 18, 'max': 65}}

if v.validate({'name': 'J', 'age': 4}):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

在示例中,我们为字符串设置了最小长度,为整数设置了最小和最大大小。

$ ./min_max.py
invalid data
{'age': ['min value is 18'], 'name': ['min length is 2']}

我们有两个验证错误。

Cerberus 正则表达式规则

我们可以使用正则表达式定义更复杂的规则。

regex.py

#!/usr/bin/env python3

from cerberus import Validator

v = Validator()
v.schema = {"contact_details": {
    "type": "dict",
    "schema": {
        "phone": {
            "type": "string",
            "minlength": 10,
            "maxlength": 10,
            "regex": "^0[0-9]{9}$"
        },
        "email": {
            "type": "string",
            "minlength": 8,
            "maxlength": 255,
            "required": True,
            "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
        }
    }
}}

if v.validate({'contact_details': {'phone': '0901123123',
                                   'email': 'john.doe@example.com'}}):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

在示例中,我们使用正则表达式为phoneemail字段定义验证规则。

Cerberus 值强制转换

值强制使我们可以在数据验证之前将可调用对象应用于值。 可调用对象的返回值替换文档中的新值。 在验证之前,可以使用强制转换数据或清除数据。

coercing.py

#!/usr/bin/env python3

from cerberus import Validator
from datetime import datetime

def to_date(s):
    return datetime.strptime(s, '%Y-%m-%d')

v = Validator()
v.schema = {'start_date': {'type': 'datetime', 'coerce': to_date}}

if v.validate({'start_date': '2019-12-11'}):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

if v.validate({'start_date': '2019/12/11'}):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

在示例中,我们使用自定义to_date()函数将数据时间值转换为选定的格式。

$ ./coercing.py
valid data
invalid data
{'start_date': ["field 'start_date' cannot be coerced: time data '2019/12/11' does not
match format '%Y-%m-%d'", 'must be of datetime type']}

这是输出。

使用 YAML 文件

在下一个示例中,我们将数据存储在 YAML 文件中。

cities.yaml

cities:
  - Bratislava
  - Kosice
  - Trnava
  - Moldava
  - Trencin

该文件包含城市列表。

from_yaml.py

#!/usr/bin/env python3

from cerberus import Validator
import yaml

v = Validator()
v.schema = {'cities': {'type': 'list', 'schema': {'type': 'string'}}}

with open('cities.yaml') as f:

    data = yaml.load(f, Loader=yaml.FullLoader)
    print(data)

    if v.validate({'cities': data['cities']}):
        print('valid data')
    else:
        print('invalid data')
        print(v.errors)

我们从 YAML 文件中读取数据并进行验证。 schema规则设置针对列表的所有元素验证定义的规则。

v = Validator()
v.schema = {'cities': {'type': 'list', 'schema': {'type': 'string'}}}

cities字段必须是一个列表,并且其所有元素都必须是字符串。

Cerberus 定制验证器

我们可以通过从Validator类扩展来创建自定义验证器。

custom_validator.py

#!/usr/bin/env python3

from cerberus import Validator
from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

class PersonValidator(Validator):

     def validate_person(self, obj):

         return self.validate(obj.__dict__)

schema = {'name': { 'type': 'string', 'minlength': 2},
    'age': {'type': 'integer', 'min': 18, 'max': 65}}

v = PersonValidator(schema)

p = Person('John Doe', 2)

if v.validate_person(p):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

在示例中,我们为Person对象定义了一个自定义验证器。

在本教程中,我们展示了如何使用 Cerberus 在 Python 中验证数据。

您可能也对以下相关教程感兴趣: Python 字符串Python Jinja 教程Python 教程,或列出所有 Python 教程

Python PostgreSQL 教程

原文: http://zetcode.com/python/psycopg2/

带有psycopg2模块的 Python PostgreSQL 教程展示了如何使用psycopg2模块在 Python 中编程 PostgreSQL 数据库。

PostgreSQL

PostgreSQL 是一个功能强大的开源对象关系数据库系统。 它是一个多用户数据库管理系统。 它可以在包括 Linux,FreeBSD,Solaris,Microsoft Windows 和 Mac OS X 在内的多个平台上运行。PostgreSQL 由 PostgreSQL 全球开发小组开发。

psycopg2模块

PostgreSQL 有几个 Python 库。 语言。 在本教程中,我们使用psycopg2模块。 它是用于 Python 编程语言的 PostgreSQL 数据库适配器。 它主要在 C 中作为libpq包装器实现。

$ pip install psycopg2

我们安装psycopg2模块。

Python psycopg2 版本示例

在第一个代码示例中,我们获得 PostgreSQL 数据库的版本。

version.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

con = None

try:

    con = psycopg2.connect(database='testdb', user='postgres',
        password='s$cret')

    cur = con.cursor()
    cur.execute('SELECT version()')

    version = cur.fetchone()[0]
    print(version)

except psycopg2.DatabaseError as e:

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

在程序中,我们连接到先前创建的testdb数据库。 我们执行一条 SQL 语句,该语句返回 PostgreSQL 数据库的版本。

import psycopg2

psycopg2是一个 Python 模块,用于与 PostgreSQL 数据库一起使用。

con = None

我们将con变量初始化为None。 如果无法创建与数据库的连接(例如磁盘已满),则不会定义连接变量。 这将导致finally子句中的错误。

con = psycopg2.connect(database='testdb', user='postgres',
    password='s$cret')

connect()方法创建一个新的数据库会话并返回一个连接对象。 用户创建时没有密码。 在 localhost 上,我们可以省略password选项。 否则,必须指定它。

cur = con.cursor()
cur.execute('SELECT version()')

从连接中,我们得到游标对象。 游标用于遍历结果集中的记录。 我们调用游标的execute()方法并执行 SQL 语句。

version = cur.fetchone()[0]

我们获取数据。 由于只检索一条记录,因此我们称为fetchone()方法。

print(version)

我们将检索到的数据打印到控制台。

except psycopg2.DatabaseError as e:

    print(f'Error {e}')
    sys.exit(1)

如果发生异常,我们将输出一条错误消息并以错误代码 1 退出程序。

finally:

    if con:
        con.close())

在最后一步,我们释放资源。

$ version.py
PostgreSQL 11.1, compiled by Visual C++ build 1914, 64-bit

这是输出。

在第二个示例中,我们再次获得 PostgreSQL 数据库的版本。 这次我们使用with关键字。

version2.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
    password='s$cret')

with con:

    cur = con.cursor()
    cur.execute('SELECT version()')

    version = cur.fetchone()[0]
    print(version)

程序返回 PostgreSQL 数据库的当前版本。 与with关键字一起使用。 代码更紧凑。

with con:

使用with关键字,Python 自动释放资源。 它还提供错误处理。

Python psycopg2 execute

我们创建cars表并在其中插入几行。 execute()执行数据库操作(查询或命令)。

create_table.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                       password='s$cret')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id SERIAL PRIMARY KEY, name VARCHAR(255), price INT)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Audi', 52642)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Mercedes', 57127)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Skoda', 9000)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Volvo', 29000)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Bentley', 350000)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Citroen', 21000)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Hummer', 41400)")
    cur.execute("INSERT INTO cars(name, price) VALUES('Volkswagen', 21600)")

该程序将创建cars表,并将八行插入到该表中。

cur.execute("CREATE TABLE cars(id SERIAL PRIMARY KEY, name VARCHAR(20), price INT)")

该 SQL 语句创建一个新的cars表。 该表有三列。

cur.execute("INSERT INTO cars(name, price) VALUES('Audi', 52642)")
cur.execute("INSERT INTO cars(name, price) VALUES('Mercedes', 57127)")

这两行将两辆车插入表。

$ psql -U postgres testdb
psql (11.1)
Type "help" for help.

testdb=# SELECT * FROM cars;
    id |    name    | price
----+------------+--------
    1 | Audi       |  52642
    2 | Mercedes   |  57127
    3 | Skoda      |   9000
    4 | Volvo      |  29000
    5 | Bentley    | 350000
    6 | Citroen    |  21000
    7 | Hummer     |  41400
    8 | Volkswagen |  21600
(8 rows)

我们使用psql工具验证写入的数据。

Python Psycopg2 executemany

executemany()方法是一种方便的方法,用于针对在提供的序列中找到的所有参数元组或映射启动数据库操作(查询或命令)。 该函数对更新数据库的命令最有用:查询返回的任何结果集都将被丢弃。

execute_many.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

cars = (
    (1, 'Audi', 52642),
    (2, 'Mercedes', 57127),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Citroen', 21000),
    (7, 'Hummer', 41400),
    (8, 'Volkswagen', 21600)
)

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS cars")
    cur.execute("CREATE TABLE cars(id SERIAL PRIMARY KEY, name VARCHAR(255), price INT)")

    query = "INSERT INTO cars (id, name, price) VALUES (%s, %s, %s)"
    cur.executemany(query, cars)

    con.commit()

本示例删除cars表(如果存在)并(重新)创建它。

cur.execute("DROP TABLE IF EXISTS cars")
cur.execute("CREATE TABLE cars(id SERIAL PRIMARY KEY, name VARCHAR(255), price INT)")

第一个 SQL 语句删除cars表(如果存在)。 第二条 SQL 语句创建cars表。

query = "INSERT INTO cars (id, name, price) VALUES (%s, %s, %s)"

这是我们使用的查询。

cur.executemany(query, cars)

我们使用便捷的executemany()方法将八行插入到表中。 此方法的第一个参数是参数化的 SQL 语句。 第二个参数是数据,以元组的形式显示。

Python psycopg2最后插入的行 ID

psycopg2不支持lastrowid属性。 要返回最后插入的行的 ID,我们必须使用 PostgreSQL 的RETURNING id子句。

lastrowid.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                       password='s$cret')

with con:

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS words")
    cur.execute("CREATE TABLE words(id SERIAL PRIMARY KEY, word VARCHAR(255))")
    cur.execute("INSERT INTO words(word) VALUES('forest') RETURNING id")
    cur.execute("INSERT INTO words(word) VALUES('cloud') RETURNING id")
    cur.execute("INSERT INTO words(word) VALUES('valley') RETURNING id")

    last_row_id = cur.fetchone()[0]

    print(f"The last Id of the inserted row is {last_row_id}")

该程序将创建一个新的words表并打印最后插入的行的 ID。

$ lastrowid.py
The last Id of the inserted row is 3

这是输出。

Python Psycopg2 fetchall

fetchall()获取查询结果的所有(剩余)行,并将它们作为元组列表返回。 如果没有更多记录可获取,则返回一个空列表。

fetch_all.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    rows = cur.fetchall()

    for row in rows:
        print(f"{row[0]} {row[1]} {row[2]}")

在此示例中,我们从cars表中检索所有数据。

cur.execute("SELECT * FROM cars")

该 SQL 语句从cars表中选择所有数据。

rows = cur.fetchall()

fetchall()方法获取所有记录。 它返回一个结果集。 从技术上讲,它是一个元组的元组。 每个内部元组代表表中的一行。

for row in rows:
    print(f"{row[0]} {row[1]} {row[2]}")

我们将数据逐行打印到控制台。

$ fetch_all.py
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

这是示例的输出。

Python Psycopg2 fetchone

fetchone()返回查询结果集的下一行,返回单个元组,或者在没有更多数据可用时返回None

fetchone.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cars")

    while True:

        row = cur.fetchone()

        if row == None:
            break

        print(f"{row[0]} {row[1]} {row[2]}")

在此示例中,我们连接到数据库并一张一张地获取cars表的行。

while True:

我们从while循环访问数据。 当我们读取最后一行时,循环终止。

row = cur.fetchone()

if row == None:
    break

fetchone()方法返回表的下一行。 如果没有剩余数据,则返回None。 在这种情况下,我们打破了循环。

print(f"{row[0]} {row[1]} {row[2]}")

数据以元组的形式返回。 在这里,我们从元组中选择记录。 第一个是 ID,第二个是汽车名称,第三个是汽车的价格。

Python psycopg2字典游标

默认游标检索元组中的数据。 使用字典游标,数据以 Python 字典的形式发送。 然后,我们可以通过列名称来引用数据。

dictionary_cursor

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import psycopg2.extras

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cursor = con.cursor(cursor_factory=psycopg2.extras.DictCursor)
    cursor.execute("SELECT * FROM cars")

    rows = cursor.fetchall()

    for row in rows:
        print(f"{row['id']} {row['name']} {row['price']}")

在此示例中,我们使用字典游标打印cars表的内容。

import psycopg2.extras

字典游标位于extras模块中。

cursor = con.cursor(cursor_factory=psycopg2.extras.DictCursor)

我们创建一个DictCursor

for row in rows:
    print(f"{row['id']} {row['name']} {row['price']}")

通过列名访问数据。 列名在 PostgreSQL 中(除非加引号)被折叠成小写并且区分大小写。 因此,我们必须以小写形式提供列名称。

Python psycopg2参数化查询

当使用参数化查询时,我们使用占位符,而不是直接将值写入语句。 参数化查询可提高安全性和性能。

Python psycopg2模块支持两种类型的占位符:ANSI C printf格式和 Python 扩展格式。

parameterized_query.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

uId = 1
uPrice = 62300

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()
    cur.execute("UPDATE cars SET price=%s WHERE id=%s", (uPrice, uId))

    print(f"Number of rows updated: {cur.rowcount}")

我们更新了一辆车的价格。 在此代码示例中,我们使用问号占位符。

cur.execute("UPDATE cars SET price=%s WHERE id=%s", (uPrice, uId))

字符(%s)是值的占位符。 这些值将添加到占位符。

print(f"Number of rows updated: {cur.rowcount}")

rowcount属性返回更新的行数。 在我们的情况下,一行已更新。

$ parameterized_query.py
Number of rows updated: 1

testdb=> SELECT * FROM cars WHERE id=1;
 id | name | price
----+------+-------
  1 | Audi | 62300
(1 row)

汽车的价格已更新。 我们使用psql工具检查更改。

第二个示例使用 Python 扩展格式的参数化语句。

parameterized_query2.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

uid = 3

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()

    cur.execute("SELECT * FROM cars WHERE id=%(id)s", {'id': uid } )

    row = cur.fetchone()

    print(f'{row[0]} {row[1]} {row[2]}')

我们使用pyformat参数化语句选择汽车的名称和价格。

cur.execute("SELECT * FROM cars WHERE id=%(id)s", {'id': uid } )

命名的占位符以冒号开头。

$ parameterized_query2.py
3 Skoda 9000

这是输出。

Python psycopg2迁移

mogrify是 Python DB API 的psycopg2扩展,在参数绑定后返回查询字符串。 返回的字符串恰好是将发送到运行execute()方法或类似方法的数据库的字符串。

mogrify.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')
cur = None

with con:

    cur = con.cursor()     

    print(cur.mogrify("SELECT name, price FROM cars WHERE id=%s", (2,)))

    # cur.execute("SELECT name, price FROM cars WHERE id=%s", (2,) )
    # row = cur.fetchone()
    # print(f"{row[0]} {row[1]}")

在将参数与mogrify()绑定后,程序显示SELECT查询字符串。

$ mogrify.py
b'SELECT name, price FROM cars WHERE id=2'

这是输出。

Python psycopg2插入图像

在本节中,我们将图像插入 PostgreSQL 数据库。

testdb=> CREATE TABLE images(id SERIAL PRIMARY KEY, data BYTEA);

对于此示例,我们创建一个名为images的新表。 对于图像,我们使用BYTEA数据类型。 它允许存储二进制字符串。

insert_image.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

def readImage():

    fin = None

    try:
        fin = open("sid.jpg", "rb")
        img = fin.read()
        return img

    except IOError as e:

        print(f'Error {e.args[0]}, {e.args[1]}')
        sys.exit(1)

    finally:

        if fin:
            fin.close()

con = None

try:
    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    cur = con.cursor()
    data = readImage()
    binary = psycopg2.Binary(data)
    cur.execute("INSERT INTO images(data) VALUES (%s)", (binary,))

    con.commit()

except psycopg2.DatabaseError as e:

    if con:
        con.rollback()

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

在程序中,我们从当前工作目录中读取图像,并将其写入 PostgreSQL testdb数据库的images表中。

try:
    fin = open("sid.jpg", "rb")
    img = fin.read()
    return img

我们从文件系统读取二进制数据。 我们有一个名为sid.jpg的 JPEG 图像。

binary = psycopg2.Binary(data)

使用psycopg2 Binary对象对数据进行编码。

cur.execute("INSERT INTO images(data) VALUES (%s)", (binary,))

该 SQL 语句用于将映像插入数据库。

Python psycopg2读取图像

在本节中,我们将执行相反的操作。 我们从数据库表中读取图像。

read_image.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

def writeImage(data):

    fout = None

    try:
        fout = open('sid2.jpg', 'wb')
        fout.write(data)

    except IOError as e:

        print(f"Error {0}")
        sys.exit(1)

    finally:

        if fout:
            fout.close()

try:
    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    cur = con.cursor()
    cur.execute("SELECT data FROM images LIMIT 1")
    data = cur.fetchone()[0]

    writeImage(data)

except psycopg2.DatabaseError as e:

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

我们从图像表中读取图像数据并将其写入另一个文件,我们称为sid2.jpg

try:
    fout = open('sid2.jpg', 'wb')
    fout.write(data)

我们以写入模式打开一个二进制文件。 来自数据库的数据被写入文件。

cur.execute("SELECT data FROM images LIMIT 1")
data = cur.fetchone()[0]

这两行从images表中选择并获取数据。 我们从第一行获取二进制数据。

Python PostgreSQL 元数据

元数据是有关数据库中数据的信息。 PostgreSQL 数据库中的元数据包含有关表和列的信息,我们在其中存储数据。 受 SQL 语句影响的行数是元数据。 结果集中返回的行数和列数也属于元数据。

可以使用游标对象的description属性或information_schema表获取 PostgreSQL 中的元数据。

接下来,我们打印cars表中的所有行及其列名。

column_names.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                       password='s$cret')

with con:

    cur = con.cursor()

    cur.execute('SELECT * FROM cars')

    col_names = [cn[0] for cn in cur.description]
    rows = cur.fetchall()

    print(f'{col_names[0]} {col_names[1]} {col_names[2]}')

我们将cars表的内容打印到控制台。 现在,我们也包括列的名称。

col_names = [cn[0] for cn in cur.description]

我们从游标对象的description属性获得列名。

print(f'{col_names[0]} {col_names[1]} {col_names[2]}')

此行打印cars表的三个列名。

我们使用for循环打印行。 数据与列名对齐。

$ column_names.py
id name price

这是输出。

在下面的示例中,我们列出了testdb数据库中的所有表。

list_tables.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2

con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

with con:

    cur = con.cursor()
    cur.execute("""SELECT table_name FROM information_schema.tables
        WHERE table_schema = 'public'""")

    rows = cur.fetchall()

    for row in rows:
        print(row[0])

该代码示例将当前数据库中的所有可用表打印到终端。

cur.execute("""SELECT table_name FROM information_schema.tables
    WHERE table_schema = 'public'""")

表名存储在系统information_schema表中。

$ list_tables.py
cars
countries
projects
employees
users
tasks
images

这些是我们系统上的表。

Python psycopg2导出和导入数据

我们可以使用copy_to()copy_from()导出和导入数据。

copy_to.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

con = None
fout = None

try:

    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    cur = con.cursor()
    fout = open('cars.csv', 'w')
    cur.copy_to(fout, 'cars', sep="|")

except psycopg2.DatabaseError as e:

    print(f'Error {e}')
    sys.exit(1)

except IOError as e:

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

    if fout:
        fout.close()

该代码示例将数据从cars表复制到cars.csv文件中。

fout = open('cars.csv', 'w')

我们打开一个文件,在其中写入cars表中的数据。

cur.copy_to(fout, 'cars', sep="|")

copy_to方法将数据从cars表复制到打开的文件。 这些列用|字符分隔。

$ cat cars.csv
2|Mercedes|57127
3|Skoda|9000
4|Volvo|29000
5|Bentley|350000
6|Citroen|21000
7|Hummer|41400
8|Volkswagen|21600
1|Audi|62300

这些是cars文件的内容。

现在,我们将执行反向操作。 我们将转储的表重新导入数据库表。

testdb=> DELETE FROM cars;
DELETE 8

我们从cars表中删除数据。

copy_from.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

con = None
f = None

try:

    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    cur = con.cursor()
    f = open('cars.csv', 'r')

    cur.copy_from(f, 'cars', sep="|")
    con.commit()

except psycopg2.DatabaseError as e:

    if con:
        con.rollback()

    print(f'Error {e}')
    sys.exit(1)

except IOError as e:

    if con:
        con.rollback()

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

    if f:
        f.close()

在程序中,我们读取cars文件的内容,并将其复制回cars表。

f = open('cars.csv', 'r')
cur.copy_from(f, 'cars', sep="|")
con.commit()

我们打开cars.csv文件进行读取,然后将内容复制到cars表中。 所做的更改已提交。

SELECT * FROM cars;
 id |    name    | price
----+------------+--------
  2 | Mercedes   |  57127
  3 | Skoda      |   9000
  4 | Volvo      |  29000
  5 | Bentley    | 350000
  6 | Citroen    |  21000
  7 | Hummer     |  41400
  8 | Volkswagen |  21600
  1 | Audi       |  62300
(8 rows)

输出显示我们已经成功地重新创建了已保存的cars表。

Python psycopg2事务

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

psycopg2模块中,事务由连接类处理。 连接游标的第一个命令启动事务。 (我们不需要用BEGINEND语句来封装 SQL 命令来创建事务。这将由psycopg2自动处理。)以下命令在此新事务的上下文中执行。 发生错误时,事务将中止,并且直到rollback()方法都不再执行其他命令。

psycopg2模块的文档说,该连接负责终止其事务,并调用commit()rollback()方法。 提交的更改将立即持久化到数据库中。 使用close()方法关闭连接或破坏连接对象(使用del或使其超出范围)将导致隐式的rollback()调用。

psycopg2模块还支持自动提交模式,其中对表的所有更改均立即生效。 为了在自动提交模式下运行,我们将连接对象的autocommit属性设置为True

no_commit.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

con = None

try:

    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id SERIAL PRIMARY KEY, name VARCHAR(255))")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")

    con.commit()

except psycopg2.DatabaseError as e:

    if con:
        con.rollback()

    print('Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

我们创建friends表,并尝试用数据填充它。 但是,正如我们将看到的,数据不会被提交。

#con.commit()

注释commit()方法。 如果我们取消注释该行,则数据将被写入表中。

finally:

    if con:
        con.close()

finally块始终执行。 如果我们尚未提交更改,并且没有发生错误(会回滚更改),则该事务仍将打开。 使用close()方法关闭连接,并通过隐式调用rollback()方法终止事务。

testdb=# \dt
        List of relations
Schema |   Name    | Type  |  Owner
--------+-----------+-------+----------
public | cars      | table | postgres
public | countries | table | postgres
public | employees | table | postgres
public | images    | table | postgres
public | projects  | table | postgres
public | tasks     | table | postgres
public | users     | table | postgres
(7 rows)

仅在取消注释该行之后,才创建friends表。

Python psycopg2自动提交

在自动提交模式下,将立即执行一条 SQL 语句。

autocommit.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psycopg2
import sys

con = None

try:

    con = psycopg2.connect(database='testdb', user='postgres',
                    password='s$cret')

    con.autocommit = True

    cur = con.cursor()

    cur.execute("DROP TABLE IF EXISTS friends")
    cur.execute("CREATE TABLE friends(id serial PRIMARY KEY, name VARCHAR(10))")
    cur.execute("INSERT INTO friends(name) VALUES ('Jane')")
    cur.execute("INSERT INTO friends(name) VALUES ('Tom')")
    cur.execute("INSERT INTO friends(name) VALUES ('Rebecca')")
    cur.execute("INSERT INTO friends(name) VALUES ('Jim')")
    cur.execute("INSERT INTO friends(name) VALUES ('Robert')")
    cur.execute("INSERT INTO friends(name) VALUES ('Patrick')")

except psycopg2.DatabaseError as e:

    print(f'Error {e}')
    sys.exit(1)

finally:

    if con:
        con.close()

在此示例中,我们以自动提交模式连接到数据库。 我们既不调用commit(),也不调用rollback()方法。

con.autocommit = True

我们将连接设置为自动提交模式。

$ autocommit.py

testdb=# select * from friends;
 id |  name
----+---------
  1 | Jane
  2 | Tom
  3 | Rebecca
  4 | Jim
  5 | Robert
  6 | Patrick
(6 rows)

数据已成功提交到friends表。

这是 PostgreSQL Python 教程。 您可能也对 SQLite Python 教程MySQL Python 教程感兴趣

PyMongo 教程

原文: http://zetcode.com/python/pymongo/

PyMongo 教程展示了如何使用 Python 编程 MongoDB。 在作者的 Github 仓库中提供了代码示例。

MongoDB 是 NoSQL 跨平台的面向文档的数据库。 它是可用的最受欢迎的数据库之一。 MongoDB 由 MongoDB Inc. 开发,并作为免费和开源软件发布。

MongoDB 中的记录是一个文档,它是由字段和值对组成的数据结构。 MongoDB 文档与 JSON 对象相似。 字段的值可以包括其他文档,数组和文档数组。 MongoDB 将文档存储在集合中。 集合类似于关系数据库中的表以及行中的文档。

游标是对查询结果集的引用。 客户可以遍历游标以检索结果。 默认情况下,游标闲置十分钟后会超时。

PyMongo

PyMongo 是一个 Python 模块,用于在 Python 中使用 MongoDB。

安装 PyMongo

以下命令用于安装 PyMongo。

$ sudo pip install pymongo

我们用pip安装 PyMongo。

创建一个 MongoDB 数据库

mongo工具是 MongoDB 的交互式 JavaScript Shell 界面,它为系统管理员提供了一个界面,并为开发者提供了一种直接测试数据库查询和操作的方法。

$ mongo testdb
MongoDB shell version: 2.6.10
connecting to: testdb
> show dbs
admin   (empty)
local   0.078GB
test    0.078GB
testdb  0.078GB

我们创建一个testdb数据库。

PyMongo 创建集合

在第一个示例中,我们创建一个新集合。 MongoDB 将文档存储在集合中。 集合类似于关系数据库中的表。

create_collection.py

#!/usr/bin/python3

from pymongo import MongoClient

cars = [ {'name': 'Audi', 'price': 52642},
    {'name': 'Mercedes', 'price': 57127},
    {'name': 'Skoda', 'price': 9000},
    {'name': 'Volvo', 'price': 29000},
    {'name': 'Bentley', 'price': 350000},
    {'name': 'Citroen', 'price': 21000},
    {'name': 'Hummer', 'price': 41400},
    {'name': 'Volkswagen', 'price': 21600} ]

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    db.cars.insert_many(cars)

该示例创建一个新的cars集合。 它包含八个文档。

cars = [ {'name': 'Audi', 'price': 52642},
    {'name': 'Mercedes', 'price': 57127},
    {'name': 'Skoda', 'price': 9000},
    {'name': 'Volvo', 'price': 29000},
    {'name': 'Bentley', 'price': 350000},
    {'name': 'Citroen', 'price': 21000},
    {'name': 'Hummer', 'price': 41400},
    {'name': 'Volkswagen', 'price': 21600} ]

该 Python 字典存储了八个要插入到 MongoDB 集合中的记录。

client = MongoClient('mongodb://localhost:27017/')

MongoClient用于与 MongoDB 通信。 我们传递MongoClient主机名和端口号。

db = client.testdb

我们获得了对testdb数据库的引用。

db.cars.insert_many(cars)

使用insert_many()方法,我们将八个文档插入到cars集合中,该集合也会自动创建。

> db.cars.find()
{ "_id" : ObjectId("5b41eb21b9c5d915989d48a8"), "price" : 52642, "name" : "Audi" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48a9"), "price" : 57127, "name" : "Mercedes" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48aa"), "price" : 9000, "name" : "Skoda" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48ab"), "price" : 29000, "name" : "Volvo" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48ac"), "price" : 350000, "name" : "Bentley" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48ad"), "price" : 21000, "name" : "Citroen" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48ae"), "price" : 41400, "name" : "Hummer" }
{ "_id" : ObjectId("5b41eb21b9c5d915989d48af"), "price" : 21600, "name" : "Volkswagen" }

我们使用mongo工具验证数据。

PyMongo 列出集合

使用collection_names(),我们获得数据库中可用列表的列表。

list_collections.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb
    print(db.collection_names())

该示例在testdb数据库中打印集合。

PyMongo 删除集合

drop()方法从数据库中删除一个集合。

drop_collection.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    db.cars.drop()

该示例从testdb数据库中删除cars集合。

PyMongo 运行命令

我们可以使用command()向 MongoDB 发出命令。 serverStatus命令返回 MongoDB 服务器的状态。

server_status.py

#!/usr/bin/python3

from pymongo import MongoClient
from pprint import pprint

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    status = db.command("serverStatus")
    pprint(status)

该示例显示冗长的服务器状态。

dbstats命令返回反映单个数据库使用状态的统计信息。

db_stats.py

#!/usr/bin/python3

from pymongo import MongoClient
from pprint import pprint

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb
    print(db.collection_names())

    status = db.command("dbstats")
    pprint(status)

该示例打印testdb的数据库统计信息。

PyMongo 游标

find方法返回一个 PyMongo 游标,该游标是对查询结果集的引用。

cursor.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    cars = db.cars.find()

    print(cars.next())
    print(cars.next())
    print(cars.next())

    cars.rewind()

    print(cars.next())
    print(cars.next())
    print(cars.next())    

    print(list(cars))

在示例中,我们使用游标。

cars = db.cars.find()

find()方法返回一个 PyMongo 游标。

print(cars.next())

使用next()方法,我们从结果集中获取下一个文档。

cars.rewind()

rewind()方法将游标倒回其未求值状态。

print(list(cars))

使用list()方法,我们可以将游标转换为 Python 列表。 它将所有数据加载到内存中。

PyMongo 读取所有数据

在下面的示例中,我们从集合中读取所有记录。 我们使用 Python for循环遍历返回的游标。

all_cars.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    cars = db.cars.find()

    for car in cars:
        print('{0} {1}'.format(car['name'], 
            car['price']))

该示例从集合中打印所有汽车名称及其价格。

cars = db.cars.find()

find()方法选择集合或视图中的文档,然后将游标返回到所选文档。 游标是对查询结果集的引用。

for car in cars:
    print('{0} {1}'.format(car['name'], 
        car['price']))

使用 Python for 循环,我们遍历结果集。

$ ./all_cars.py 
Audi 52642
Mercedes 57127
Skoda 9000
Volvo 29000
Bentley 350000
Citroen 21000
Hummer 41400
Volkswagen 21600

这是输出。

PyMongo 计数文件

使用count()方法检索文档数量。

count_cars.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    n_cars = db.cars.find().count()

    print("There are {} cars".format(n_cars))

该示例使用count()计算集合中的汽车数量。

$ ./count_cars.py 
There are 8 cars

集合中有八辆车。

PyMongo 过滤器

find()find_one()的第一个参数是一个过滤器。 过滤器是所有文档必须匹配的条件。

filtering.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    expensive_cars = db.cars.find({'price': {'$gt': 50000}})

    for ecar in expensive_cars:
        print(ecar['name'])

该示例打印价格大于 50000 的汽车的名称。

expensive_cars = db.cars.find({'price': {'$gt': 50000}})

find()方法的第一个参数是所有返回的记录必须匹配的过滤器。 过滤器使用$gt运算符仅返回昂贵的汽车。

$ ./filtering.py 
Audi
Mercedes
Bentley

这是输出。

PyMongo 投影

通过投影,我们可以从返回的文档中选择特定字段。 投影在find()方法的第二个参数中传递。

projection.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    cars = db.cars.find({}, {'_id': 1, 'name':1})

    for car in cars:
        print(car)

该示例打印文档的_idname字段。

cars = db.cars.find({}, {'_id': 1, 'name':1})

我们可以指定包含或排除投影,但不能同时指定。

$ ./projection.py 
{'name': 'Audi', '_id': ObjectId('5b41eb21b9c5d915989d48a8')}
{'name': 'Mercedes', '_id': ObjectId('5b41eb21b9c5d915989d48a9')}
{'name': 'Skoda', '_id': ObjectId('5b41eb21b9c5d915989d48aa')}
{'name': 'Volvo', '_id': ObjectId('5b41eb21b9c5d915989d48ab')}
{'name': 'Bentley', '_id': ObjectId('5b41eb21b9c5d915989d48ac')}
{'name': 'Citroen', '_id': ObjectId('5b41eb21b9c5d915989d48ad')}
{'name': 'Hummer', '_id': ObjectId('5b41eb21b9c5d915989d48ae')}
{'name': 'Volkswagen', '_id': ObjectId('5b41eb21b9c5d915989d48af')}

这是输出。

PyMongo 排序文件

我们可以使用sort()对文档进行排序。

sorting.py

#!/usr/bin/python3

from pymongo import MongoClient, DESCENDING

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    cars = db.cars.find().sort("price", DESCENDING)

    for car in cars:
        print('{0} {1}'.format(car['name'], 
            car['price']))

该示例按价格降序对记录进行排序。

$ ./sorting.py 
Bentley 350000
Mercedes 57127
Audi 52642
Hummer 41400
Volvo 29000
Volkswagen 21600
Citroen 21000
Skoda 9000

这是输出。

PyMongo 聚合

聚合计算集合中数据的聚合值。

aggregate_sum.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    agr = [ {'$group': {'_id': 1, 'all': { '$sum': '$price' } } } ]

    val = list(db.cars.aggregate(agr))

    print('The sum of prices is {}'.format(val[0]['all']))

该示例计算所有汽车价格的总和。

agr = [ {'$group': {'_id': 1, 'all': { '$sum': '$price' } } } ]

$sum运算符计算并返回数值的总和。 $group运算符通过指定的标识符表达式对输入文档进行分组,并将累加器表达式(如果指定)应用于每个组。

val = list(db.cars.aggregate(agr))

aggregate()方法将聚合操作应用于cars集合。

$ ./aggregate_sum.py 
The sum of prices is 581769

所有值的总和是 581769。

我们可以使用$match运算符来选择要汇总的特定汽车。

sum_two_cars.py

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    agr = [{ '$match': {'$or': [ { 'name': "Audi" }, { 'name': "Volvo" }] }}, 
        { '$group': {'_id': 1, 'sum2cars': { '$sum': "$price" } }}]

    val = list(db.cars.aggregate(agr))

    print('The sum of prices of two cars is {}'.format(val[0]['sum2cars']))

该示例计算奥迪和沃尔沃汽车的价格总和。

agr = [{ '$match': {'$or': [ { 'name': "Audi" }, { 'name': "Volvo" }] }}, 
    { '$group': {'_id': 1, 'sum2cars': { '$sum': "$price" } }}]

该表达式使用$match$or$group$sum运算符执行任务。

$ ./sum_two_cars.py 
The sum of prices of two cars is 81642

两辆车的总价是 81642。

PyMongo 限制数据输出

limit查询选项指定要返回的文档数量,skip()选项指定某些文档。

MongoSkipLimit.java

#!/usr/bin/python3

from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    cars = db.cars.find().skip(2).limit(3)

    for car in cars:
        print('{0}: {1}'.format(car['name'], car['price']))

该示例从cars集合中读取,跳过了前两个文档,并将输出限制为三个文档。

cars = db.cars.find().skip(2).limit(3)

skip()方法跳过前两个文档,limit()方法将输出限制为三个文档。

$ ./limit_documents.py 
Skoda: 9000
Volvo: 29000
Bentley: 350000

这是示例的输出。

在 PyMongo 教程中,我们使用了 MongoDB 和 Python。

您可能还会对以下教程感兴趣: PyMySQL 教程pyDAL 教程Peewee 教程SQLite Python 教程OpenPyXL 教程Bottle 教程Python CSV 教程Python 教程

PyMySQL 教程

原文: http://zetcode.com/python/pymysql/

PyMySQL 教程展示了如何使用 PyMySQL 模块在 Python 中对 MySQL 进行编程。

PyMySQL

PyMySQL 是基于 PEP 249 的纯 Python MySQL 客户端库。大多数公共 API 与mysqlclientMySQLdb兼容。 PyMySQL 可与 MySQL 5.5+和 MariaDB 5.5+ 一起使用。

MySQL 是领先的开源数据库管理系统。 它是一个多用户,多线程的数据库管理系统。 MySQL 在网络上特别流行。

PyMySQL 安装

$ sudo pip3 install PyMySQL

我们使用pip3工具安装 PyMySQL。

PyMySQL 版本示例

在下面的示例中,我们获取 MySQL 的版本。

version.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql

con = pymysql.connect('localhost', 'user17', 
    's$cret', 'mydb')

with con:

    cur = con.cursor()
    cur.execute("SELECT VERSION()")

    version = cur.fetchone()

    print("Database version: {}".format(version[0]))

在 MySQL 中,我们可以使用SELECT VERSION()获取 MySQL 的版本。

import pymysql

我们导入pymysql模块。

con = pymysql.connect('localhost', 'user17', 
    's$cret', 'mydb')

我们使用connect()连接到数据库。 我们传递四个参数:主机名,MySQL 用户名,密码和数据库名。

with con:

使用with关键字,Python 解释器会自动释放资源。 它还提供错误处理。

cur = con.cursor()

从连接对象,我们创建一个游标。 游标用于遍历结果集中的记录。

cur.execute("SELECT VERSION()")

我们调用游标的execute()方法并执行 SQL 语句。

version = cur.fetchone()

fetchone()方法获取查询结果集的下一行,返回单个序列,或者在没有更多数据可用时返回None

print("Database version: {}".format(version[0]))

我们打印数据库的版本。

$ ./version.py 
Database version: 5.7.23-0ubuntu0.16.04.1

这是输出。

PyMySQL fetchAll

fetchAll()方法检索查询结果的所有(剩余)行,并将它们作为序列序列返回。

retrieve_all.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql

con = pymysql.connect('localhost', 'user17', 
    's$cret', 'testdb')

with con: 

    cur = con.cursor()
    cur.execute("SELECT * FROM cities")

    rows = cur.fetchall()

    for row in rows:
        print("{0} {1} {2}".format(row[0], row[1], row[2]))

在示例中,我们从数据库表中检索所有城市。

cur.execute("SELECT * FROM cities")

该 SQL 语句从citys表中选择所有数据。

rows = cur.fetchall()

fetchall()方法获取所有记录。 它返回一个结果集。 从技术上讲,它是一个元组的元组。 每个内部元组代表表中的一行。


for row in rows:
    print("{0} {1} {2}".format(row[0], row[1], row[2]))

我们将数据逐行打印到控制台。

$ ./retrieve_all.py 
1 Bratislava 432000
2 Budapest 1759000
3 Prague 1280000
4 Warsaw 1748000
5 Los Angeles 3971000
6 New York 8550000
7 Edinburgh 464000
8 Berlin 3671000

这是输出。

PyMySQL 字典游标

默认游标以元组的元组返回数据。 当我们使用字典游标时,数据以 Python 字典的形式发送。 这样,我们可以通过列名称来引用数据。

dictionary_cursor.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql
import pymysql.cursors

con = pymysql.connect(host='localhost',
        user='user17',
        password='s$cret',
        db='mydb',
        charset='utf8mb4',
        cursorclass=pymysql.cursors.DictCursor)

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cities")

    rows = cur.fetchall()

    for row in rows:
        print(row["id"], row["name"])

在此示例中,我们使用字典游标获取citys表的第一行。

con = pymysql.connect(host='localhost',
        user='user17',
        password='s$cret',
        db='mydb',
        charset='utf8mb4',
        cursorclass=pymysql.cursors.DictCursor)

connect()方法中,我们将pymysql.cursors.DictCursor值传递给cursorclass参数。

for row in rows:
    print(row["id"], row["name"])

我们通过cities表的列名称引用数据。

PyMySQL 列标题

接下来,我们将展示如何使用数据库表中的数据打印列标题。

column_headers.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql

con = pymysql.connect('localhost', 'user17', 
    's$cret', 'testdb')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cities")

    rows = cur.fetchall()

    desc = cur.description

    print("{0:>3} {1:>10}".format(desc[0][0], desc[1][0]))

    for row in rows:    
        print("{0:3} {1:>10}".format(row[0], row[2]))

列名称被认为是元数据。 它们是从游标对象获得的。

desc = cur.description

游标的description属性返回有关查询的每个结果列的信息。

print("{0:>3} {1:>10}".format(desc[0][0], desc[1][0]))

在这里,我们打印并格式化表格的列名。


for row in rows:    
    print("{0:3} {1:>10}".format(row[0], row[2]))

我们遍历并打印数据。

$ ./column_headers.py 
 id       name
  1     432000
  2    1759000
  3    1280000
  4    1748000
  5    3971000
  6    8550000
  7     464000
  8    3671000

这是输出。

PyMySQL 预备语句

在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

prepared_statement.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql

con = pymysql.connect('localhost', 'user17', 
    's$cret', 'testdb')

# user input
myid = 4

with con:    

    cur = con.cursor()

    cur.execute("SELECT * FROM cities WHERE id=%s", myid) 

    cid, name, population  = cur.fetchone()
    print(cid, name, population)

在示例中,我们获得具有指定 ID 的行。

cur.execute("SELECT * FROM cities WHERE id=%s", myid) 

我们使用由%s标记标识的占位符。 在执行 SQL 语句之前,将值绑定到它们的占位符。

$ ./prepared_statement.py 
4 Warsaw 1748000

这是输出。

PyMySQL 受影响的行

rowcount是只读的游标属性,它指定最后一条SELECTUPDATEINSERT语句产生的行数。

affected_rows.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import pymysql

con = pymysql.connect('localhost', 'user17',
   's$cret', 'mydb')

with con:

    cur = con.cursor()
    cur.execute("SELECT * FROM cities WHERE id IN (1, 2, 3)")

    # rows = cur.fetchall()

    # for row in rows:
    #     print("{0} {1} {2}".format(row[0], row[1], row[2]))

    print("The query affected {} rows".format(cur.rowcount))

在示例中,我们有一条SELECT语句选择三行。

print("The query affected {} rows".format(cur.rowcount))

我们生成一条消息,显示受影响的行数。

$ ./affected_rows.py 
The query affected 3 rows

这是输出。

在本教程中,我们一直在使用 PyMySQL 模块在 Python 中编写 MySQL 数据库。

您可能也对以下相关教程感兴趣: Python SQLite 教程Python PostgreSQL 教程PyMongo 教程Peewee 教程pyDAL 教程Python 教程,或列出 Python 教程

Peewee 教程

原文: http://zetcode.com/python/peewee/

Peewee 教程展示了如何使用 Python Peewee ORM。

对象关系映射(ORM)是一种从面向对象的语言访问关系数据库的技术。 它是 Python 数据库 API 的抽象。

Python Peewee

Peewee 是一个简单而小型的 Python ORM 工具。 它支持 SQLite,MySQL 和 PostgreSQL。

$ pipenv install peewee

我们安装peewee模块。

Peewee 映射

Model映射到数据库表,Field映射到表列,instance映射到表行。

Peewee 对于 MySQL 使用MySQLDatabase,对于 PostgreSQL 使用PostgresqlDatabase,对于 SQLite 使用SqliteDatabase。 在本教程中,我们将使用 SQLite 数据库。

Peewee 字段类型

Peewee 模型中的字段类型定义模型的存储类型。 它们被转换为相应的数据库列类型。

字段类型 SQLite PostgreSQL MySQL
CharField varchar varchar varchar
TextField text text longtext
DateTimeField datetime timestamp longtext
IntegerField integer integer integer
BooleanField smallint bool bool
FloatField real real real
DoubleField real double double
BigIntegerField integer bigint bigint
DecimalField decimal numeric numeric
PrimaryKeyField integer serial integer
ForeignKeyField integer integer integer
DateField date date date
TimeField time time time

下表列出了 Peewee 字段类型以及相应的 SQLite,PostgreSQL 和 MySQL 列类型。

Peewee 模型定义

在第一个示例中,我们创建一个简单的数据库表。

model_definition.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

Note.create_table()

note1 = Note.create(text='Went to the cinema')
note1.save()

note2 = Note.create(text='Exercised in the morning',
        created=datetime.date(2018, 10, 20))
note2.save()

note3 = Note.create(text='Worked in the garden',
        created=datetime.date(2018, 10, 22))
note3.save()

note4 = Note.create(text='Listened to music')
note4.save()

该示例在 SQLite 中创建notes数据库表。

db = peewee.SqliteDatabase('test.db')

我们启动test.db SQLite 数据库。 这将在文件系统上创建一个test.db文件。

class Note(peewee.Model):
...

我们定义了一个名为Note的数据库模型。 Peewee 模型继承自peewee.Model

text = peewee.CharField()
created = peewee.DateField(default=datetime.date.today)

我们指定模型字段。 我们有一个CharField和一个DateFieldCharField是用于存储字符串的字段类。 DateField是用于存储日期的字段类。 如果未指定,则采用默认值。

class Meta:
    database = db
    db_table = 'notes'

Meta类中,我们定义对数据库的引用和数据库表名称。

Note.create_table()

该表是使用create_table()从模型创建的。

note1 = Note.create(text='Went to the cinema')
note1.save()

我们创建并保存一个新实例。

sqlite> select * from notes;
1|Went to the cinema|2018-11-01
2|Exercised in the morning|2018-10-20
3|Worked in the garden|2018-10-22
4|Listened to music|2018-11-01

我们验证数据。

Peewee 删除表

使用drop_table()模型方法删除该表。

drop_table.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:
        database = db
        db_table = 'notes'

Note.drop_table()

该示例删除notes表。

Peewee insert_many

insert_many()方法允许进行批量创建。

insert_many.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

Note.create_table()

data = [
    { 'text': 'Tai chi in the morning', 'created': datetime.date(2018, 10, 20) },
    { 'text': 'Visited friend', 'created': datetime.date(2018, 10, 12) },
    { 'text': 'Went to cinema', 'created': datetime.date(2018, 10, 5) },
    { 'text': 'Listened to music', 'created': datetime.date(2018, 10, 28) },
    { 'text': 'Watched TV all day', 'created': datetime.date(2018, 10, 14) },
    { 'text': 'Worked in the garden', 'created': datetime.date(2018, 10, 22) },
    { 'text': 'Walked for a hour', 'created': datetime.date(2018, 10, 28) }
]

with db.atomic():

    query = Note.insert_many(data)
    query.execute()

该代码示例通过一次批量创建操作来重新创建notes表。

data = [
    { 'text': 'Tai chi in the morning', 'created': datetime.date(2018, 10, 20) },
    { 'text': 'Visited friend', 'created': datetime.date(2018, 10, 12) },
    { 'text': 'Went to cinema', 'created': datetime.date(2018, 10, 5) },
    { 'text': 'Listened to music', 'created': datetime.date(2018, 10, 28) },
    { 'text': 'Watched TV all day', 'created': datetime.date(2018, 10, 14) },
    { 'text': 'Worked in the garden', 'created': datetime.date(2018, 10, 22) },
    { 'text': 'Walked for a hour', 'created': datetime.date(2018, 10, 28) }
]

我们在字典列表中定义数据。

with db.atomic():

    query = Note.insert_many(data)
    query.execute()

我们执行批量操作。 atomic()方法将批量操作置于事务中。

Peewee 选择所有实例

select()方法用于检索定义的模型的实例。

fetch_all.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

notes = Note.select()

for note in notes:
    print('{} on {}'.format(note.text, note.created))

该示例获取并显示所有Note实例。

notes = Note.select()

select()方法创建一个SELECT查询。 如果未明确提供任何字段,则查询将默认选择模型上定义的所有字段。

$ ./fetch_all.py
Tai chi in the morning on 2018-10-20
Visited friend on 2018-10-12
Went to cinema on 2018-10-05
Listened to music on 2018-10-28
Watched TV all day on 2018-10-14
Worked in the garden on 2018-10-22
Walked for a hour on 2018-10-28

这是输出。

Peewee where过滤器

where()方法可以根据给定条件过滤数据。

where_clause.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

notes = Note.select().where(Note.id > 3)

for note in notes:
    print('{} {} on {}'.format(note.id, note.text, note.created))

该示例检索 ID 大于三的所有行。

notes = Note.select().where(Note.id > 3)

where()方法对查询应用过滤条件。

$ ./where_clause.py
4 Listened to music on 2018-10-28
5 Watched TV all day on 2018-10-14
6 Worked in the garden on 2018-10-22
7 Walked for a hour on 2018-10-28

这是输出。

Peewee 多个where表达式

我们可以组合多个where表达式。

multiple_where_expr.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

notes = Note.select().where((Note.id > 2) & (Note.id < 6))

for note in notes:
    print('{} {} on {}'.format(note.id, note.text, note.created))

该示例检索id大于 2 且小于 6 的所有行。

notes = Note.select().where((Note.id > 2) & (Note.id < 6))

我们将两个where表达式与&运算符结合使用。

$ ./multiple_where_expr.py
3 Went to cinema on 2018-10-05
4 Listened to music on 2018-10-28
5 Watched TV all day on 2018-10-14

这是输出。

Peewee 检索单个实例

选择单个实例有两种方法: 它们每个都使用get()方法。

single_instance.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

note1 = Note.select().where(Note.text == 'Went to cinema').get()

print(note1.id)
print(note1.text)
print(note1.created)

note2 = Note.get(Note.text == 'Listened to music')

print(note2.id)
print(note2.text)
print(note2.created)

该示例显示了如何以两种方式检索单个实例。

note1 = Note.select().where(Note.text == 'Went to cinema').get()

我们可以使用Note.select().where().get()方法链。

note2 = Note.get(Note.text == 'Listened to music')

还有一个Note.get()快捷方式,其功能相同。

$ ./single_instance.py
3
Went to cinema
2018-10-05
4
Listened to music
2018-10-28

这是输出。

Peewee 选择特定的列

select()方法内部,我们可以指定要包含在查询中的列的名称。

columns.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

notes = Note.select(Note.text, Note.created).limit(2)

output = [e for e in notes.tuples()]
print(output)

该示例包括两列:textcreated。 该 ID 被跳过。 我们将查询限制为两个实例。

$ ./columns.py
[('Tai chi in the morning', datetime.date(2018, 10, 20)),
    ('Visited friend', datetime.date(2018, 10, 12))]

这是输出。

Peewee 计数实例

要计算表中的模型实例数,我们可以使用count()方法。

count_instances.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

n = Note.select().count()
print(n)

n2 = Note.select().where(Note.created >= datetime.date(2018, 10, 20)).count()
print(n2)

该示例计算所有实例的数量以及日期等于或晚于2018/10/20的实例的数量。

$ ./count_instances.py
7
4

这是输出。

Peewee 显示 SQL 语句

可以使用sql()方法显示生成的 SQL 语句。

show_sql.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

note3 = Note.select().where(Note.id == 3)
print(note3.sql())

该示例显示将 ORM 查询转换为的 SQL。

$ ./show_sql.py
('SELECT "t1"."id", "t1"."text", "t1"."created" FROM "notes" AS "t1"
    WHERE ("t1"."id" = ?)', [3])

这是输出。

Peewee offsetlimit

通过offsetlimit属性,我们可以定义实例的初始跳过和要包含在select()中的实例数。

offset_limit.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'notes'

notes = Note.select().offset(2).limit(3)

for note in notes:
    print(note.id, note.text, note.created)

该示例从第二个实例开始返回三个实例。

$ ./offset_limit.py
3 Went to cinema 2018-10-05
4 Listened to music 2018-10-28
5 Watched TV all day 2018-10-14

这是输出。

Peewee 排序

可以使用order_by()对检索到的实例进行排序。

order_by.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db 
        db_table = 'notes'

print('Ascending order')
print('*****************************')

notes = Note.select(Note.text, Note.created).order_by(Note.created)

for note in notes:
    print(note.text, note.created)

print()
print('Descending order')
print('*****************************')

notes = Note.select(Note.text, Note.created).order_by(Note.created.desc())

for note in notes:
    print(note.text, note.created)

该代码示例按创建日期对实例进行排序。

notes = Note.select(Note.text, Note.created).order_by(Note.created)

该行返回按创建日期升序排列的笔记实例。

notes = Note.select(Note.text, Note.created).order_by(Note.created.desc())

要按升序检索笔记,我们在字段上附加desc()方法。

Ascending order
*****************************
Went to cinema 2018-10-05
Visited friend 2018-10-12
Watched TV all day 2018-10-14
Tai chi in the morning 2018-10-20
Worked in the garden 2018-10-22
Listened to music 2018-10-28
Walked for a hour 2018-10-28

Descending order
*****************************
Listened to music 2018-10-28
Walked for a hour 2018-10-28
Worked in the garden 2018-10-22
Tai chi in the morning 2018-10-20
Watched TV all day 2018-10-14
Visited friend 2018-10-12
Went to cinema 2018-10-05

这是音符实例的有序列表。

Peewee 删除实例

delete_by_id()方法删除由其 ID 标识的实例。 它返回已删除实例的数量。

delete_by_id.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:
        database = db
        db_table = 'notes'

n2 = Note.delete_by_id(1)
print(n2)

该示例删除一个 ID 为 1 的Note实例。

Peewee 删除多个实例

要删除更多实例,我们调用delete()方法。 它返回成功删除的实例数。

delete_instances.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:
        database = db
        db_table = 'notes'

query = Note.delete().where(Note.id > 3)
n = query.execute()

print('{} instances deleted'.format(n))

在此示例中,我们删除了 ID 大于 3 的所有实例。

$ ./delete_instances.py
4 instances deleted

在我们的例子中,我们删除了四个Note实例。

Peewee 更新实例

update()方法更新一个实例。 它返回成功更新的实例数。

update_instance.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Note(peewee.Model):

    text = peewee.CharField()
    created = peewee.DateField(default=datetime.date.today)

    class Meta:
        database = db
        db_table = 'notes'

query = Note.update(created=datetime.date(2018, 10, 27)).where(Note.id == 1)
n = query.execute()

print('# of rows updated: {}'.format(n))

该示例使用 ID 1 修改笔记的创建日期。

Peewee 一对多关系

在以下示例中,我们将模型映射到现有表。 使用ForeignKeyField创建模型之间的关系。

customers_reservations.sql

BEGIN TRANSACTION;
DROP TABLE IF EXISTS reservations;
DROP TABLE IF EXISTS customers;

CREATE TABLE IF NOT EXISTS customers(id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO customers(Name) VALUES('Paul Novak');
INSERT INTO customers(Name) VALUES('Terry Neils');
INSERT INTO customers(Name) VALUES('Jack Fonda');
INSERT INTO customers(Name) VALUES('Tom Willis');

CREATE TABLE IF NOT EXISTS reservations(id INTEGER PRIMARY KEY, 
    customer_id INTEGER, created DATE, 
    FOREIGN KEY(customer_id) REFERENCES customers(id));
INSERT INTO reservations(customer_id, created) VALUES(1, '2018-22-11');
INSERT INTO reservations(customer_id, created) VALUES(2, '2018-28-11');
INSERT INTO reservations(customer_id, created) VALUES(2, '2018-29-11');
INSERT INTO reservations(customer_id, created) VALUES(1, '2018-29-11');
INSERT INTO reservations(customer_id, created) VALUES(3, '2018-02-12');
COMMIT;

该 SQL 创建两个表:customersreservations。 两个表之间存在一对多的关系:一个客户可以进行很多预订。

sqlite> .read customers_reservations.sql 

我们将 SQL 文件读入数据库。

one2many.py

#!/usr/bin/env python3

import peewee
import datetime

db = peewee.SqliteDatabase('test.db')

class Customer(peewee.Model):

    name = peewee.TextField()

    class Meta:

        database = db
        db_table = 'customers'

class Reservation(peewee.Model):

    customer = peewee.ForeignKeyField(Customer, backref='reservations')
    created = peewee.DateField(default=datetime.date.today)

    class Meta:

        database = db
        db_table = 'reservations'

customer = Customer.select().where(Customer.name == 'Paul Novak').get()

for reservation in customer.reservations:

    print(reservation.id)
    print(reservation.created)

在示例中,我们定义了两个映射到表的模型。 然后,我们选择一个客户并显示其预订。

customer = peewee.ForeignKeyField(Customer, backref='reservations')

CustomerReservation模型之间的关系是通过ForeignKeyField创建的。 backref属性设置了我们如何引用客户的预订。

for reservation in customer.reservations:

客户实例具有reservations属性,其中包含相应的预留。

$ ./one2many.py
1
2018-22-11
4
2018-29-11

这是输出。

在本教程中,我们介绍了 Python Peewee ORM。

您可能也对以下相关教程感兴趣: Python 教程pyDAL 教程PyMongo 教程PyMySQL 教程

pyDAL 教程

原文: http://zetcode.com/python/pydal/

pyDAL 教程展示了如何使用 pyDAL 数据库抽象层在 Python 中对数据库进行编程。 我们在代码示例中使用 SQLite。

pyDAL

pyDAL 是纯 Python 数据库抽象层。 pyDAL 模块以指定的方言为数据库后端动态生成 SQL。 生成的代码可在不同类型的数据库之间移植。

pyDAL 安装

$ sudo pip3 install pyDAL

我们使用pip3工具安装 pyDAL。

pyDAL 创建数据库表

在下面的示例中,我们创建一个数据库表。

create_table.py

#!/usr/bin/env python3

from pydal import DAL, Field

db = DAL('sqlite://test.db', folder='dbs')

try:
    db.define_table('cars', Field('name'), Field('price', type='integer'))
    db.cars.insert(name='Audi', price=52642)
    db.cars.insert(name='Skoda', price=9000)
    db.cars.insert(name='Volvo', price=29000)
    db.cars.insert(name='Bentley', price=350000)
    db.cars.insert(name='Citroen', price=21000)
    db.cars.insert(name='Hummer', price=41400)
    db.cars.insert(name='Volkswagen', price=21600)

finally:

    if db:
        db.close()

该示例创建一个具有七行的cars表。

db = DAL('sqlite://test.db', folder='dbs')

DAL代表数据库连接。 它以数据库连接字符串作为第一个参数。 我们连接到 SQLite 数据库。

db.define_table('cars', Field('name'), Field('price', type='integer'))

数据库表由define_table()定义。 如果不存在,则创建它。 它有两个字段:名称和价格。 ID 字段会自动生成。

db.cars.insert(name='Audi', price=52642)

我们使用insert()将新行插入表中。 在db连接的cars表上调用该方法。

$ ls dbs
c95cf9bab36fcb04c2424cdf9be0f6e3_cars.table  sql.log  test.db

除了test.db数据库之外,我们还有一个扩展名为.table的迁移文件和一个日志文件。

pyDAL 删除表

使用drop()删除了数据库表。

drop_table.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    cars = db.define_table('cars', Field('name'), Field('price', 'integer'))

    cars.drop()

finally:

    if db:
        db.close() 

在示例中,我们使用drop()方法删除cars表。

pyDAL 选择行

使用select()选择表行。

select_all_rows.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price'))

    rows = db().select(db.cars.ALL)

    for row in rows:
        print("{} {} {}".format(row['id'], row['name'], row['price']))

finally:

    if db:
        db.close()      

在示例中,我们从cars表中检索所有行。

rows = db().select(db.cars.ALL)

我们使用select()方法获取所有行。 db.cars.ALL指示从表中选择所有列。

for row in rows:
    print("{} {} {}".format(row['id'], row['name'], row['price']))

我们遍历每一行并打印其字段。

$ ./select_all_cars.py 
1 Audi 52642
2 Skoda 9000
3 Volvo 29000
4 Bentley 350000
5 Citroen 21000
6 Hummer 41400
7 Volkswagen 21600

这是输出。

pyDAL 排序

以下示例显示如何使用pyDAL排序数据。

order_by.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    rows = db(db.cars).select(orderby=db.cars.price)

    for row in rows:
        print("{} {} {}".format(row['id'], row['name'], row['price']))

    print("**************************************")        

    rows = db(db.cars).select(orderby=~db.cars.price)

    for row in rows:
        print("{} {} {}".format(row['id'], row['name'], row['price']))

finally:

    if db:
        db.close()         

该示例打印表中的所有行,并按价格升序和降序对其进行排序。

rows = db(db.cars).select(orderby=db.cars.price)

通过select()方法的orderby参数进行排序。

rows = db(db.cars).select(orderby=~db.cars.price)

要按降序排序,我们使用波浪字符。

$ ./order_by.py 
5 Citroen 21000
7 Volkswagen 21600
3 Volvo 29000
4 Bentley 350000
6 Hummer 41400
1 Audi 52642
2 Skoda 9000
**************************************
2 Skoda 9000
1 Audi 52642
6 Hummer 41400
4 Bentley 350000
3 Volvo 29000
7 Volkswagen 21600
5 Citroen 21000

这是输出。

pyDAL 限制数据输出

可以使用select()方法的limitby参数限制数据输出。

limit_by.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    rows = db(db.cars).select(limitby=(2, 5))

    for row in rows:
        print("{} {} {}".format(row['id'], row['name'], row['price']))

finally:

    if db:
        db.close() 

在代码示例中,我们将输出限制为偏移量为 2 的三行。

$ ./limit_by.py 
3 Volvo 29000
4 Bentley 350000
5 Citroen 21000

这是输出。

pyDAL 计数行

使用count(),我们可以获得表中的行数。

count_rows.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    n = db(db.cars.id).count()

    print("There are {} rows in the table".format(n))

finally:

    if db:
        db.close()     

在示例中,我们打印cars表中的行数。

$ ./count_rows.py 
There are 7 rows in the table

表中有七行。

pyDAL JSON 输出

我们可以使用as_json()获得 JSON 格式的数据。

json_output.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    rows = db(db.cars).select()
    print(rows.as_json())

finally:

    if db:
        db.close()        

该示例以 JSON 格式显示所有行。

$ ./json_output.py 
[{"id": 1, "price": 52642, "name": "Audi"}, 
{"id": 2, "price": 9000, "name": "Skoda"}, 
{"id": 3, "price": 29000, "name": "Volvo"}, 
{"id": 4, "price": 350000, "name": "Bentley"}, 
{"id": 5, "price": 21000, "name": "Citroen"}, 
{"id": 6, "price": 41400, "name": "Hummer"}, 
{"id": 7, "price": 21600, "name": "Volkswagen"}]

这是输出。

pyDAL 最后一个 SQL

pyDAL 最后执行的 SQL 可以通过_lastsql()找到。

lastsql.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    # we ignore the result
    db(db.cars.id).select(db.cars.name, db.cars.price)

    print(db._lastsql)

finally:

    if db:
        db.close()      

在示例中,我们在执行select语句时打印 pyDAL 执行的 SQL。

$ ./lastsql.py 
('SELECT "cars"."name", "cars"."price" FROM "cars" WHERE ("cars"."id" IS NOT NULL);', 0.0005686283111572266)

该 SQL 由 pyDAL 生成。

pyDAL 执行原始 SQL

我们可以使用executesql()方法执行原始 SQL。

raw_sql.py

#!/usr/bin/env python3

from pydal import DAL, Field

try:

    db = DAL('sqlite://test.db', folder='dbs')
    db.define_table('cars', Field('name'), Field('price', 'integer'))

    data = db.executesql('SELECT * FROM cars WHERE id=6')[0]

    print(data)

finally:

    if db:
        db.close() 

在示例中,我们使用executesql()执行 SQL SELECT语句。

$ ./raw_sql.py 
(6, 'Hummer', '41400')

这是输出。

在本教程中,我们使用 pyDAL 处理 SQLite 数据库。

您可能也对以下相关教程感兴趣: PyMongo 教程Peewee 教程PyMySQL 教程SQLite Python 教程Python 教程

Pytest 教程

原文: http://zetcode.com/python/pytest/

Pytest 教程展示了如何使用 Pytest 模块测试 Python 应用。

python Pytest

Pytest 是用于测试 Python 应用的 Python 库。 它是鼻子测试和单元测试的替代方法。

Pytest 安装

使用以下命令安装 Pytest:

$ pip install pytest 

这将安装 Pytest 库。

Pytest 测试发现约定

如果未指定任何参数,则在testpaths(如果已配置)或当前目录中的位置搜索测试文件。 另外,命令行参数可以在目录,文件名或节点 ID 的任何组合中使用。

Pytest 在所选目录中查找test_*.py*_test.py文件。 在选定的文件中,Pytest 在类之外查找带前缀的测试函数,并在带前缀的测试类中查找带前缀的测试方法(无__init__()方法)。

运行 Pytest

Pytest 不带任何参数,将查看当前工作目录(或其他一些预配置的目录)以及测试文件的所有子目录,并运行找到的测试代码。

$ pytest

运行当前目录中的所有测试文件。

$ pytest min_max_test.py

我们可以通过指定名称作为参数来运行特定的测试文件。

$ pytest min_max_test.py::test_min

可以通过在::字符后提供其名称来运行特定函数。

$ pytest -m smoke

标记可用于对测试进行分组。 然后使用pytest -m运行一组标记的测试。

$ pytest -k <expression>

另外,我们可以使用表达式来运行与测试函数和类的名称匹配的测试。

Python Pytest 简单示例

在第一个示例中,我们将使用 Pytest 测试两个简单的数学算法。

algo.py

def max(values):

  _max = values[0]

  for val in values:
      if val > _max:
          _max = val

  return _max

def min(values):

  _min = values[0]

  for val in values:
      if val < _min:
          _min = val

  return _min

我们有一个带有自定义max()min()函数的模块。

min_max_test.py

#!/usr/bin/env python3

import algo

def test_min():
    values = (2, 3, 1, 4, 6)

    val = algo.min(values)
    assert val == 1

def test_max():
    values = (2, 3, 1, 4, 6)

    val = algo.max(values)
    assert val == 6

测试文件min_max_test.py的名称中包含一个测试词。

def test_min():
  values = (2, 3, 1, 4, 6)

  val = algo.min(values)
  assert val == 1

此外,测试函数test_min()具有测试字。 我们使用assert关键字来测试算法的值。

$ pytest min_max_test.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Jano\Documents\pyprogs\pytest
collected 2 items

min_max_test.py ..                                                                                               [100%]

============================================== 2 passed in 0.03 seconds ===============================================

这是输出。 有两个测试,并且都成功通过了。 pytest -v min_max_test.py显示了更详细的输出。

Pytest 跳过

使用跳过装饰器,我们可以跳过指定的测试。 跳过测试有多种原因。 例如,数据库/在线服务目前不可用,或者我们跳过了 Windows 上针对 Linux 的特定测试。

skipping.py

#!/usr/bin/env python3

import algo
import pytest

@pytest.mark.skip
def test_min():
    values = (2, 3, 1, 4, 6)

    val = algo.min(values)
    assert val == 1

def test_max():
    values = (2, 3, 1, 4, 6)

    val = algo.max(values)
    assert val == 6

在示例中,test_min()被跳过。

$ pytest min_max_test.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Jano\Documents\pyprogs\pytest
collected 2 items

min_max_test.py s.                                                                                               [100%]

========================================= 1 passed, 1 skipped in 0.04 seconds =========================================

在测试文件名后面的输出中,s代表跳过的和。 通过。

Pytest 标记

我们可以使用标记将测试组织为单元。

marking.py

#!/usr/bin/env python3

# pytest -m a marking.py
# pytest -m b marking.py

import pytest

@pytest.mark.a
def test_a1():

    assert (1) == (1)

@pytest.mark.a
def test_a2():

    assert (1, 2) == (1, 2)

@pytest.mark.a
def test_a3():

    assert (1, 2, 3) == (1, 2, 3)

@pytest.mark.b
def test_b1():

    assert "falcon" == "fal" + "con"

@pytest.mark.b
def test_b2():

    assert "falcon" == f"fal{'con'}"

我们有两组由标记ab标识的测试。 这些单元由pytest -m a marking.pypytest -m b marking.py运行。

Pytest 参数化测试

通过参数化测试,我们可以向断言中添加多个值。 我们使用@pytest.mark.parametrize标记。

parametrized.py

#!/usr/bin/env python3

import algo
import pytest

@pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1), 
    ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)])
def test_min(data, expected):

    val = algo.min(data)
    assert val == expected

@pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 6), 
    ((5, -2, 0, 9, 12), 12), ((200, 100, 0, 300, 400), 400)])
def test_max(data, expected):

    val = algo.max(data)
    assert val == expected

在示例中,我们使用多个输入数据测试这两个函数。

@pytest.mark.parametrize("data, expected", [((2, 3, 1, 4, 6), 1), 
    ((5, -2, 0, 9, 12), -2), ((200, 100, 0, 300, 400), 0)])
def test_min(data, expected):

    val = algo.min(data)
    assert val == expected

我们将两个值传递给测试函数:数据和期望值。 在我们的例子中,我们用三个数据元组测试min()函数。

$ pytest parametrized.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Jano\Documents\pyprogs\pytest
collected 6 items

parametrized.py ......                                                                                           [100%]

============================================== 6 passed in 0.03 seconds ===============================================

Pytest 输出告知有六次运行。

Pytest 夹具

测试需要在一组已知对象的背景下进行。 这组对象称为测试夹具。

algo.py

def sel_sort(data):

  if not isinstance(data, list):
      vals = list(data)
  else:
      vals = data

  size = len(vals)

  for i in range(0, size):

      for j in range(i+1, size):

          if vals[j] < vals[i]:
              _min = vals[j]
              vals[j] = vals[i]
              vals[i] = _min
  return vals
...  

对于此示例,我们向algo.py模块添加了一个选择排序算法。

fixtures.py

#!/usr/bin/env python3

import algo
import pytest

@pytest.fixture
def data():

    return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]

def test_sel_sort(data):

    sorted_vals = algo.sel_sort(data)
    assert sorted_vals == sorted(data)

我们用夹具测试选择排序。

@pytest.fixture
def data():

    return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]

我们的测试装置仅返回一些测试数据。 请注意,我们通过其名称引用此灯具:data

def test_sel_sort(data):

  sorted_vals = algo.sel_sort(data)
  assert sorted_vals == sorted(data)

test_sel_sort()函数中,我们将数据夹具作为函数参数传递。

$ pytest fixtures.py
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Jano\Documents\pyprogs\pytest
collected 1 item

fixtures.py .                                                                                                    [100%]

============================================== 1 passed in 0.02 seconds ===============================================

这是输出。

Pytest 布局

Python 测试可以多种方式组织。 测试可以集成在 Python 包中,也可以放在包外。

综合测试

接下来,我们展示如何在 Python 包中运行测试。

setup.py
utils
│   algo.py
│   srel.py
│   __init__.py
│
└───tests
        algo_test.py
        srel_test.py
        __init__.py

我们有这种包装布局。 测试与包一起位于tests子目录中。

setup.py

#!/usr/bin/env python3

from setuptools import setup, find_packages

setup(name="utils", packages=find_packages())

这是setup.py

utils/algo.py

def sel_sort(data):

    if not isinstance(data, list):
        vals = list(data)
    else:
        vals = data

    size = len(vals)

    for i in range(0, size):

        for j in range(i+1, size):

            if vals[j] < vals[i]:
                _min = vals[j]
                vals[j] = vals[i]
                vals[i] = _min
    return vals

def max(values):

    _max = values[0]

    for val in values:
        if val > _max:
            _max = val

    return _max

def min(values):

    _min = values[0]

    for val in values:
        if val < _min:
            _min = val

    return _min

这是algo.py文件。

utils/srel.py

def is_palindrome(val):

    return val == val[::-1]

我们还有另一个模块,其中包含一个测试单词是否为回文的函数。

tests/algo_test.py

#!/usr/bin/env python3

import utils.algo
import pytest

@pytest.fixture
def data():

    return [3, 2, 1, 5, -3, 2, 0, -2, 11, 9]

def test_sel_sort(data):

    sorted_vals = utils.algo.sel_sort(data)
    assert sorted_vals == sorted(data)

def test_min():
    values = (2, 3, 1, 4, 6)

    val = utils.algo.min(values)
    assert val == 1

def test_max():
    values = (2, 3, 1, 4, 6)

    val = utils.algo.max(values)
    assert val == 6

这些是utils.algo模块的测试。 注意,我们使用完整的模块名称。

tests/srel_test.py

#!/usr/bin/env python3

import utils.srel
import pytest

@pytest.mark.parametrize("word, expected", [('kayak', True), 
    ('civic', True), ('forest', False)])
def test_palindrome(word, expected):

    val = utils.srel.is_palindrome(word)
    assert val == expected

这是对is_palindrome()函数的测试。

utils/__init__.py

utils/tests/__init__.py

两个__init__.py文件均为空。

$ pytest --pyargs utils
================================================= test session starts =================================================
platform win32 -- Python 3.7.0, pytest-5.0.1, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\Jano\Documents\pyprogs\pytest\structure
collected 6 items

utils\tests\algo_test.py ...                                                                                     [ 50%]
utils\tests\srel_test.py ...                                                                                     [100%]

============================================== 6 passed in 0.06 seconds ===============================================

我们使用pytest --pyargs utils命令运行测试。

外部测试

下一个示例显示了应用源布局,其中测试未集成在包内。

setup.py
src
└───utils
│       algo.py
│       srel.py
tests
    algo_test.py
    srel_test.py

在这种布局中,我们在源代码树之外进行测试。 请注意,不需要__init__.py文件。

$ set PYTHONPATH=src
$ pytest

我们设置PYTHONPATH并运行 Pytest。

在本教程中,我们介绍了 Python Pytest 库。

您可能也对以下相关教程感兴趣: Django 电子邮件教程Python Jinja 教程Python 教程,或列出所有 Python 教程

交互式 Python

原文: http://zetcode.com/lang/python/interactivepython/

在 Python 编程教程的这一部分中,我们讨论交互式 Python 解释器。

Python 代码可以通过两种基本方式启动。 作为脚本或在交互式解释器中。

#!/usr/bin/env python

# first.py

print("The Python tutorial")

这是一个小型 Python 脚本的示例。 它是从 UNIX Shell 启动的。

$ ./first.py 
The Python tutorial

交互解释器

运行 Python 代码的另一种方法是交互式 Python 解释器。 Python 解释器对于我们的探索非常有用。 当我们快速想要测试 Python 语言的一些基本功能并且不想编写整个脚本时。 为了获得交互式解释器,我们在最喜欢的 shell 上执行 Python 命令。

$ python3
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

这是 Python 解释器的欢迎消息。 我们在机器上看到了 Python 版本。 在我们的例子中是 Python 3.5.2。 ">>>"是在 Python 交互模式下使用的提示。 要离开解释器并返回外壳,我们可以输入 Ctrl + Dquit()。 键入 Ctrl + L 将清除 Python 解释器的屏幕。

现在我们可以查询一些有用的信息。

>>> credits
    Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
    for supporting Python development.  See www.python.org for more information.

如果键入credits,我们将获得有关参与 Python 开发的组织的一些信息。

>>> copyright
Copyright (c) 2001-2016 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.

copyright命令提供 Python 编程语言的版权。

license()命令提供了有关 Python 许可证的多个页面。

获得帮助

help命令提供了有关 Python 的一些帮助。

>>> help
Type help() for interactive help, or help(object) for help about object.
>>>

我们可以通过两种方式使用该命令。 我们可以获取有关特定对象的帮助,或者进入交互式帮助模式。

例如,如果我们键入help(True),我们将获得有关bool对象的一些信息。

Help on bool object:

class bool(int)
 |  bool(x) -> bool
 |  
 |  Returns True when the argument x is true, False otherwise.
 |  The builtins True and False are the only two instances of the class bool.
 |  The class bool is a subclass of the class int, and cannot be subclassed.
 |  
 |  Method resolution order:
 |      bool
 |      int
 |      object
 |  
 |  Methods defined here:
 |  
 |  __and__(...)
 |      x.__and__(y) <==> x&y

 ...

如果主题大于一页,我们可以使用箭头滚动主题。 如果要退出该主题,请按 q 键。

如果键入help(),将获得解释器的交互式帮助模式。

>>> help()

Welcome to Python 3.5's help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/3.5/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics".  Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".

help> 

要退出帮助模式并返回解释器,我们使用quit命令。

keywords命令提供 Python 编程语言中可用关键字的列表。

help> keywords

Here is a list of the Python keywords.  Enter any keyword to get more help.

False               def                 if                  raise
None                del                 import              return
True                elif                in                  try
and                 else                is                  while
as                  except              lambda              with
assert              finally             nonlocal            yield
break               for                 not                 
class               from                or                  
continue            global              pass  

如果我们输入任何关键字,我们都会得到一些帮助。

modules命令给出了可用模块的列表。 同样,键入模块名称将提供其他帮助。

最后,我们有topics命令。

help> topics

Here is a list of available topics.  Enter any topic name to get more help.

ASSERTION           DELETION            LOOPING             SHIFTING
ASSIGNMENT          DICTIONARIES        MAPPINGMETHODS      SLICINGS
ATTRIBUTEMETHODS    DICTIONARYLITERALS  MAPPINGS            SPECIALATTRIBUTES
ATTRIBUTES          DYNAMICFEATURES     METHODS             SPECIALIDENTIFIERS
AUGMENTEDASSIGNMENT ELLIPSIS            MODULES             SPECIALMETHODS
BASICMETHODS        EXCEPTIONS          NAMESPACES          STRINGMETHODS
BINARY              EXECUTION           NONE                STRINGS
BITWISE             EXPRESSIONS         NUMBERMETHODS       SUBSCRIPTS
BOOLEAN             FLOAT               NUMBERS             TRACEBACKS
CALLABLEMETHODS     FORMATTING          OBJECTS             TRUTHVALUE
CALLS               FRAMEOBJECTS        OPERATORS           TUPLELITERALS
CLASSES             FRAMES              PACKAGES            TUPLES
CODEOBJECTS         FUNCTIONS           POWER               TYPEOBJECTS
COMPARISON          IDENTIFIERS         PRECEDENCE          TYPES
COMPLEX             IMPORTING           PRIVATENAMES        UNARY
CONDITIONAL         INTEGER             RETURNING           UNICODE
CONTEXTMANAGERS     LISTLITERALS        SCOPING             
CONVERSIONS         LISTS               SEQUENCEMETHODS     
DEBUGGING           LITERALS            SEQUENCES  

topics命令提供有关 Python 编程语言的主题列表。 在这里我们可以找到一些有用的信息。

Python 代码

接下来,我们将提供一些 Python 解释器的实际示例。

>>> 2 + 4
6
>>> 5 * 56
280
>>> 5 - 45
-40
>>> 

Python 解释器可以用作计算器。 立即执行每个表达式,结果显示在屏幕上。

>>> a = 3
>>> b = 4
>>> a**b
81
>>> a == b
False
>>> a < b
True
>>>

我们可以定义变量并对其执行操作。

>>> import random
>>> dir(random)
['BPF', 'LOG4', 'NV_MAGICCONST', 'RECIP_BPF', 'Random', 'SG_MAGICCONST', 
'SystemRandom', 'TWOPI', 'WichmannHill', '_BuiltinMethodType', '_MethodType',
'__all__', '__builtins__', '__doc__', '__file__', '__name__', '_acos', 
'_ceil', '_cos', '_e', '_exp', '_hexlify', '_inst', '_log', '_pi', '_random',
'_sin', '_sqrt', '_test', '_test_generator', '_urandom', '_warn', 
'betavariate', 'choice', 'expovariate', 'gammavariate', 'gauss', 
'getrandbits', 'getstate', 'jumpahead', 'lognormvariate', 'normalvariate',
'paretovariate', 'randint', 'random', 'randrange', 'sample', 'seed',
'setstate', 'shuffle', 'uniform', 'vonmisesvariate', 'weibullvariate']
>>> 

在这里,我们导入了一个随机模块。 利用dir()函数,我们进一步探索了随机模块。

借助特殊的__doc__字符串,我们可以获得有关特定功能的帮助。

>>> print random.seed.__doc__
Initialize internal state from hashable object.

        None or no argument seeds from current time or from an operating
        system specific randomness source if available.

        If a is not None or an int or long, hash(a) is used instead.
>>>

locals()命令显示我们当前的本地名称空间。

>>> locals()
{'random': <module 'random' from '/usr/lib/python3.5/random.py'>, '__spec__': None, 
'__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 
'__builtins__': <module 'builtins' (built-in)>, '__doc__': None, '__name__': '__main__'}

我们可以看到我们先前导入的随机模块。

>>> class Car:
...   pass
... 
>>> def function():
...   pass
... 
>>> for i in range(5):
...   print(i)
... 
0
1
2
3
4
>>>

我们可以定义自己的类,函数或使用控制流结构。 我们一定不要忘记缩进代码。 要完成所有这些代码块,我们键入,输入两次键。

>>> import os
>>> os.getcwd()
'/home/vronskij/programming/python'
>>> os.system('ls')
command_line_arguments.py  read_input.py
0

在这里,我们导入os模块并与操作系统进行交互。

最后,我们要退出解释器。 我们可以通过几种方式退出解释器:

  • Ctrl + D
  • exit()

我们还可以以编程方式退出解释器。

>>> raise SystemExit
$ 

要么

>>> import sys
>>> sys.exit()
$

解释器已退出。

Python 之禅

Python 的 Zen 是一套有关如何编写良好的 Python 代码的规则。 它以某种方式反映了语言的哲学。

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

可以通过启动import this来读取规则。

在本章中,我们研究了 Python 交互式解释器。

{% raw %}

Bottle 教程

原文: http://zetcode.com/python/bottle/

Bottle 教程展示了如何使用 Python Bottle Web 微框架在 Python 中创建简单的 Web 应用。

Bottle

Bottle 是用于 Python 的快速,简单且轻量级的 WSGI 微型网络框架。 它作为单个文件模块分发。 除 Python 标准库外,没有其他依赖项。

Web 服务器网关接口(WSGI)是 Web 服务器的简单调用约定,用于将请求转发到以 Python 编程语言编写的 Web 应用或框架。

Bottle 安装

$ sudo pip3 install bottle

我们使用pip3工具安装 Bottle。

Bottle 简单的例子

在下面的示例中,我们创建一个简单的 Bottle 应用。

$ mkdir simple && cd simple
$ touch simple.py

我们创建一个项目目录一个 Python 文件。

simple.py

#!/usr/bin/env python3

from bottle import route, run

@route('/message')
def hello():
    return "Today is a beautiful day"  

run(host='localhost', port=8080, debug=True)

该示例向客户端发送一条消息。

from bottle import route, run

我们导入route装饰器和run函数。 route装饰器用于将函数绑定到请求 URL。 run函数启动服务器实例。 默认情况下,它是开发服务器。

@route('/message')
def hello():
    return "Today is a beautiful day"  

使用@route()装饰器,我们定义了一个路由。 路由是 URL 与 Web 服务器函数之间的映射。 在我们的例子中,该函数返回一条简单的文本消息。

run(host='localhost', port=8080, debug=True)

我们以调试模式在端口 8080 上启动服务器。

$ ./simple.py 
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://localhost:8080/
Hit Ctrl-C to quit.

我们启动开发服务器。

$ curl localhost:8080/message
Today is a beautiful day

我们使用curl工具创建一个请求。 服务器以一条简单消息响应。

Bottle JSON 响应

Web 应用通常以 JSON 格式发送响应。 Bottle 自动将 Python 字典转换为 JSON。

json_response.py

#!/usr/bin/env python3

from bottle import route, run

@route('/cars')
def getcars():

    cars = [ {'name': 'Audi', 'price': 52642},
        {'name': 'Mercedes', 'price': 57127},
        {'name': 'Skoda', 'price': 9000},
        {'name': 'Volvo', 'price': 29000},
        {'name': 'Bentley', 'price': 350000},
        {'name': 'Citroen', 'price': 21000},
        {'name': 'Hummer', 'price': 41400},
        {'name': 'Volkswagen', 'price': 21600} ]

    return dict(data=cars)

run(host='localhost', port=8080, debug=True)

该应用将有关汽车的数据作为 JSON 发送到客户端。

return dict(data=cars)

Bottle 将 Python 字典转换为 JSON。

$ curl localhost:8080/cars
{"data": [{"name": "Audi", "price": 52642}, {"name": "Mercedes", "price": 57127}, 
{"name": "Skoda", "price": 9000}, {"name": "Volvo", "price": 29000}, 
{"name": "Bentley", "price": 350000}, {"name": "Citroen", "price": 21000}, 
{"name": "Hummer", "price": 41400}, {"name": "Volkswagen", "price": 21600}]}

我们收到一个命名的 JSON 数组。

Bottle 获取请求

HTTP GET 方法请求指定资源的表示形式。 在 Bottle 中,我们可以使用@route@get装饰器映射 GET 请求。 从request.query检索数据。

GET 请求通常是默认的请求方法。

get_request.py

#!/usr/bin/env python3

from bottle import route, run, request, get

@get('/msg')
def message():

    name = request.query.name
    age = request.query.age

    return "{0} is {1} years old".format(name, age)

run(host='localhost', port=8080, debug=True)

该应用根据 GET 请求的数据构建一条消息。

@get('/msg')
def message():

message()函数通过/msg路径映射到 GET 请求。 @get('msg')装饰器等效于@route('msg', method='GET'),或更短的@route('msg')

name = request.query.name
age = request.query.age

我们从查询字符串中检索数据。

$ curl "localhost:8080/greet?name=Peter&age=34"
Peter is 34 years old

我们使用curl工具发出 GET 请求。 GET 请求是curl的默认请求。 我们将nameage参数添加到查询字符串。

Bottle 静态文件

使用static_file(),我们可以在 Bottle 中提供静态文件。

$ mkdir botstat && cd botstat
$ mkdir public 
$ touch public/home.html app.py

我们为应用创建目录和文件。

public/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home page</title>
</head>
<body>
    <p>This is home page</p>
</body>
</html>

这是位于public目录中的主页。 静态资源的目录通常称为publicstatic

app.py

#!/usr/bin/env python3

from bottle import route, run, static_file

@route('/<filepath:path>')
def server_static(filepath):
    return static_file(filepath, root='./public/')

run(host='localhost', port=8080, debug=True)

在此示例中,我们提供静态文件。 为了获得主页,我们必须导航到localhost:8080/home.html

@route('/<filepath:path>')

filepath:path是仅允许出现在包含斜杠的路径中的字符的过滤器。

return static_file(filepath, root='./public/')

通过static_file()函数,我们可以提供静态文件。 静态文件所在的目录在root参数中指定。

Bottle 过滤器

包含通配符的路由称为动态路由(与静态路由相对)。 它们可以同时匹配多个 URL。 通配符由括在尖括号中的名称组成(例如<name>),并且可以接受一个或多个字符,直到下一个斜杠为止。

过滤器可用于定义更特定的通配符。

  • :int匹配(带符号)数字
  • :float匹配十进制数字
  • :path路径段中允许使用的数学字符
  • :re允许指定自定义正则表达式

filters.py

#!/usr/bin/env python3

from bottle import route, run

@route('/app/<myid:int>/')
def provide(myid):
    return "Object with id {} returned".format(myid)

@route('/app/<name:re:[a-z]+>/')
def provide(name):
    return "Name {} given".format(name)    

run(host='localhost', port=8080, debug=True)

该示例使用整数过滤器和正则表达式过滤器。

$ curl localhost:8080/app/3/
Object with id 3 returned

在这里,我们向路径添加一个整数。

Bottle 例子

在下面的示例中,我们将表单发送到 Bottle 应用。

$ mkdir simple_form && cd simple_form
$ mkdir public 
$ touch public/index.html simple_form.py

我们为应用创建目录和文件。

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home page</title>
</head>

<body>

    <form method="post" action="doform">
        <div>
            <label for="name">Name:</label>
            <input type="text" id="name" name="name">
        </div>
        <div>
            <label for="occupation">Occupation:</label>
            <input type="text" id="occupation" name="occupation">
        </div>
        <button type="submit">Submit</button>
    </form>

</body>

</html>

在 HTML 文件中,我们有一个表单标签。 该表格包含两个输入字段:名称和职业。

simple_form.py

#!/usr/bin/env python3

from bottle import route, run, post, request, static_file

@route('/')
def server_static(filepath="index.html"):
    return static_file(filepath, root='./public/')

@post('/doform')
def process():

    name = request.forms.get('name')
    occupation = request.forms.get('occupation')
    return "Your name is {0} and you are a(n) {1}".format(name, occupation)

run(host='localhost', reloader=True, port=8080, debug=True)

simple_form.py文件中,我们提供一个表格并处理该表格。

@route('/')
def server_static(filepath="index.html"):
    return static_file(filepath, root='./public/')

对于根路径(/),我们从public目录提供index.html

@post('/doform')
def process():

    name = request.forms.get('name')
    occupation = request.forms.get('occupation')
    return "Your name is {0} and you are a(n) {1}".format(name, occupation)

在这里,我们处理表格。 我们使用@post装饰器。 我们从request.forms获取数据并构建消息字符串。

Bottle 错误处理器

可以使用@error装饰器创建自定义错误页面。

error_handler.py

#!/usr/bin/env python3

from bottle import route, run, error

@route('/app/<myid:int>')
def provide(myid):
    return "Object with id {} returned".format(myid)

@error(404)
def error404(error):
    return '404 - the requested page could not be found'   

run(host='localhost', port=8080, debug=True)

在此示例中,我们在自定义错误处理器中处理 404 错误。

@error(404)
def error404(error):
    return '404 - the requested page could not be found'   

@error装饰器将错误代码作为参数。

$ curl localhost:8080/app/Peter
404 - the requested page could not be found

我们尝试访问未定义的路由; 我们会收到自定义错误消息。

Bottle MongoDB 示例

在以下示例中,我们从 MongoDB 数据库以 JSON 形式返回数据。

create_cars.py

#!/usr/bin/python3

from pymongo import MongoClient

cars = [ {'name': 'Audi', 'price': 52642},
    {'name': 'Mercedes', 'price': 57127},
    {'name': 'Skoda', 'price': 9000},
    {'name': 'Volvo', 'price': 29000},
    {'name': 'Bentley', 'price': 350000},
    {'name': 'Citroen', 'price': 21000},
    {'name': 'Hummer', 'price': 41400},
    {'name': 'Volkswagen', 'price': 21600} ]

client = MongoClient('mongodb://localhost:27017/')

with client:

    db = client.testdb

    db.cars.insert_many(cars)

使用此脚本,我们创建一个 Mongo 集合。 有关在 Python 中使用 MongoDB 的更多信息,请参考 PyMongo 教程

bottle_mongo.py

#!/usr/bin/env python3

from bottle import route, run, HTTPResponse
from pymongo import MongoClient
import json

client = MongoClient('mongodb://localhost:27017/')

@route('/cars')
def getcars():

    db = client.testdb
    cars = list(db.cars.find({}, {'_id': 0}))

    if cars:

        return json.dumps(cars)
    else: 
        raise HTTPResponse(status=204)

run(host='localhost', port=8080, debug=True)

该示例从 Mongo 集合返回数据作为 JSON。

client = MongoClient('mongodb://localhost:27017/')

创建一个MongoClient实例。

db = client.testdb
cars = list(db.cars.find({}, {'_id': 0}))

我们从两个字段中检索所有数据; 我们排除_id字段。

if cars:

    return json.dumps(cars)
else: 
    raise HTTPResponse(status=204)

如果有数据,我们将使用json.dumps()将其转换为 JSON,然后将其返回给客户端。 否则,我们会发送 204 状态代码。

Bottle 模板示例

模板引擎是一个旨在将模板与数据模型结合以生成结果文档的库。 默认情况下,Bottle 使用简单的模板引擎。

$ mkdir botview && cd botview
$ mkdir views 
$ touch views/show_cars.tpl app.py

我们为应用创建目录和文件。

views/show_cars.tpl

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cars</title>
</head>
<body>

    <table>

        <tr>
            <th>Name</th>
            <th>Price</th>
        </tr>
        % for car in cars:
        <tr>
            <td>{{car['name']}}</td>
            <td>{{car['price']}}</td>
        </tr>
        % end

    </table>

</body>
</html>

在此模板中,我们浏览接收到的cars对象并从中生成一个表。 模板文件位于views目录中。

app.py

#!/usr/bin/env python3

from bottle import route, run, template, HTTPResponse
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')

@route('/cars')
def getcars():

    db = client.testdb
    data = db.cars.find({}, {'_id': 0})

    if data:

        return template('show_cars', cars=data)
    else: 
        return HTTPResponse(status=204)

run(host='localhost', port=8080, debug=True)

在应用中,我们从 MongoDB 集合中检索数据。 我们使用template()函数将模板文件与数据结合在一起。

在本教程中,我们使用 Bottle 在 Python 中创建简单的 Web 应用。

您可能也对以下相关教程感兴趣: PyMongo 教程Jinja 教程Python 教程

{% endraw %}

{% raw %}

Python Jinja 教程

原文: http://zetcode.com/python/jinja/

Jinja 教程展示了如何使用 Jinja 模块在 Python 中创建模板。

Python Jinja 模块

Jinja 是 Python 的模板引擎。 它类似于 Django 模板引擎。

模板引擎或模板处理器是一个旨在将模板与数据模型结合以生成文档的库。 模板引擎通常用于在源代码预处理中生成大量电子邮件,或生成动态 HTML 页面。

我们创建一个模板引擎,在其中定义静态零件和动态零件。 动态部分随后将替换为数据。 渲染功能随后将模板与数据结合在一起。

Jinja 安装

$ sudo pip3 install jinja2

我们使用pip3工具安装 Jinja。

Jinja 分隔符

Jinja 在模板字符串中使用各种分隔符。

  • {% %} - 陈述
  • {{ }} - 要打印到模板输出的表达式
  • {# #} - 模板输出中未包含的注释
  • # ## - 行语句

Jinja 简单的例子

在第一个示例中,我们创建一个非常简单的模板。

simple.py

#!/usr/bin/env python3

from jinja2 import Template

name = input("Enter your name: ")

tm = Template("Hello {{ name }}")
msg = tm.render(name=name)

print(msg)

该示例要求输入用户名并生成消息字符串,该消息字符串将打印给用户。 模板引擎类似于 Python format()方法; 但是模板引擎功能更强大,并且具有更多功能。

from jinja2 import Template

我们从jinja2模块导入Template对象。 Template是中央模板对象。 它代表一个已编译的模板,并用于对其进行求值。

tm = Template("Hello {{ name }}")

在我们的模板中,我们具有用于打印变量的{{ }}语法。 变量以render()方法传递。

msg = tm.render(name=name)

使用render()方法,我们生成最终输出。 该方法将模板字符串与作为参数传递的数据连接在一起。 传递给render()方法的变量称为上下文变量。

$ ./simple.py
Enter your name: Paul
Hello Paul

这是一个示例输出。

在下一个示例中,我们使用两个变量。

simple2.py

#!/usr/bin/env python3

from jinja2 import Template

name = 'Peter'
age = 34

tm = Template("My name is {{ name }} and I am {{ age }}")
msg = tm.render(name=name, age=age)

print(msg)

模板字符串呈现两个变量:名称和年龄。 这次变量是硬编码的。

$ ./simple2.py 
My name is Peter and I am 34

这是输出。

Jinja 对象

我们可以使用模板字符串中的对象。

objects.py

#!/usr/bin/env python3

from jinja2 import Template

class Person:

    def __init__(self, name, age):

        self.name = name
        self.age = age

    def getAge(self):
        return self.age

    def getName(self):
        return self.name    

person = Person('Peter', 34)

tm = Template("My name is {{ per.getName() }} and I am {{ per.getAge() }}")
msg = tm.render(per=person)

print(msg)

在示例中,我们定义了Person对象。 我们通过两个获取器获取名称和年龄。

字典

Jinja 允许使用方便的点表示法来访问 Python 字典中的数据。

dicts.py

#!/usr/bin/env python3

from jinja2 import Template

person = { 'name': 'Person', 'age': 34 }

tm = Template("My name is {{ per.name }} and I am {{ per.age }}")
# tm = Template("My name is {{ per['name'] }} and I am {{ per['age'] }}")
msg = tm.render(per=person)

print(msg)

我们有一本人字典。 我们使用点运算符访问字典键。

tm = Template("My name is {{ per.name }} and I am {{ per.age }}")
# tm = Template("My name is {{ per['name'] }} and I am {{ per['age'] }}")

活动方式和注释方式均有效。 点符号更方便。

Jinja 原始数据

我们可以使用rawendraw标记转义 Jinja 分隔符。

raw_data.py

#!/usr/bin/env python3

from jinja2 import Template

data = '''
{% raw %}
His name is {{ name }}
{% endraw %}
'''

tm = Template(data)
msg = tm.render(name='Peter')

print(msg)

通过使用rawendraw块,我们逃避了 Jinja {{ }}语法。 它是按字面意思印刷的。

Jinja 转义数据

为了转义诸如<>字符之类的数据,我们可以使用过滤器或escape()函数。

escape_data.py

#!/usr/bin/env python3

from jinja2 import Template, escape

data = '<a>Today is a sunny day</a>'

tm = Template("{{ data | e}}")
msg = tm.render(data=data)

print(msg)
print(escape(data))

该示例转义<>字符。

tm = Template("{{ data | e}}")

使用e过滤器,可以转储数据。 过滤器应用|字符。

print(escape(data))

转义函数执行相同的操作。

Jinja 表达式

for 表达式用于迭代模板中的数据收集。

现在,我们不再使用简单的字符串模板。 我们使用一个FileSystemLoader加载的文本文件。

for_expr.py

#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('showpersons.txt')

output = template.render(persons=persons)
print(output)

在此示例中,模板是showpersons.txt文件。 该文件位于templates目录中。

persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}
]

数据是字典列表。

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

我们定义一个FileSystemLoader。 从templates目录中检索模板。

template = env.get_template('showpersons.txt')

我们使用get_template()方法获得模板。

templates/showpersons.txt

{% for person in persons -%}
    {{ person.name }} {{ person.age }}
{% endfor %}

在模板文件中,我们使用for表达式遍历集合。 我们显示此人的姓名和年龄。 %字符旁边的破折号用于控制空格。

Jinja 条件

条件是满足特定条件时要求值的表达式。

conditionals.py

#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

persons = [
    {'name': 'Andrej', 'age': 34}, 
    {'name': 'Mark', 'age': 17}, 
    {'name': 'Thomas', 'age': 44}, 
    {'name': 'Lucy', 'age': 14}, 
    {'name': 'Robert', 'age': 23}, 
    {'name': 'Dragomir', 'age': 54}, 
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)
env.trim_blocks = True
env.lstrip_blocks = True
env.rstrip_blocks = True

template = env.get_template('showminors.txt')

output = template.render(persons=persons)
print(output)

该示例仅打印未成年人。 未成年人是指未满 18 岁的人。

env.trim_blocks = True
env.lstrip_blocks = True
env.rstrip_blocks = True

输出中的空白可以通过环境属性来控制。

templates/showminors.txt

{% for person in persons %}
    {% if person.age < 18 %}
        {{- person.name }}
    {% endif %}    
{%- endfor %}

在模板中,我们仅使用if表达式输出 18 岁以下的人。

$ ./conditionals.py
Mark
Lucy

这是输出。

Jinja sum过滤器

可以将过滤器应用于数据以对其进行修改。 例如,sum过滤器可以对数据求和,escape过滤器对它们进行转义,sort过滤器对它们进行排序。

sum_filter.py

#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

cars = [
    {'name': 'Audi', 'price': 23000}, 
    {'name': 'Skoda', 'price': 17300}, 
    {'name': 'Volvo', 'price': 44300}, 
    {'name': 'Volkswagen', 'price': 21300}
]

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('sumprices.txt')

output = template.render(cars=cars)
print(output)

在示例中,我们使用sum过滤器来计算所有汽车价格的总和。

cars = [
    {'name': 'Audi', 'price': 23000}, 
    {'name': 'Skoda', 'price': 17300}, 
    {'name': 'Volvo', 'price': 44300}, 
    {'name': 'Volkswagen', 'price': 21300}
]

我们有汽车字典的列表。 每个字典都有一个价格键。 它将用于计算总和。

templates/sumprices.txt

The sum of car prices is {{ cars | sum(attribute='price') }}

在模板文件中,我们将过滤器应用于汽车集合对象。 根据price属性计算总和。

$ ./sum_filter.py 
The sum of car prices is 105900

这是输出。

Jinja 模板继承

模板继承是一项强大的功能,可减少代码重复并改善代码组织。 我们定义了一个基本模板,其他模板文件也从中继承。 这些模板文件将覆盖基本模板文件的特定块。

ineritance.py

#!/usr/bin/env python3

from jinja2 import Environment, FileSystemLoader

content = 'This is about page'

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('about.html')

output = template.render(content=content)
print(output)

我们渲染about.html文件。 它继承自base.html文件。

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    {% block content%}

    {% endblock %}
</body>
</html>

base.html文件中,我们声明两个块:标题和内容。 这些块将在子模板中填充特定的标签和文本。

about.html

{% extends 'base.html' %}

{% block title%}About page{% endblock %}

{% block content %}
<h1>About page</h1>
<p>
    This is about page
</p>
{% endblock %}

about.html模板文件继承自base.html。 它添加了特定于此页面的数据。 我们避免代码重复; 我们不会在两个页面上重复相同的标签,例如bodyhtmlmeta标签。

{% extends 'base.html' %}

继承是通过extends指令完成的。

{% block title%}About page{% endblock %}

我们定义一个标题。

{% block content %}
<h1>About page</h1>
<p>
    This is about page
</p>
{% endblock %}

并且我们定义内容。

Jinja Flask 示例

在下一个示例中,我们创建一个使用 Jinja 的简单 Flask 应用。

app.py

#!/usr/bin/env python3

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route("/greet")
def greet():
    username = request.args.get('name')
    return render_template('index.html', name=username)

if __name__ == "__main__":
    app.run()

在此 Flask 应用中,我们获取用户名,并将其作为参数传递给render_template()方法。 greet()函数对/greet路径做出反应。

templates/index.html

<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Greeting</title>
</head>

<body>
    <p>
        Hello {{ name }}
    </p>
</body>

</html>

这是模板文件,位于templates目录中。 我们使用{{ name }}语法将用户名添加到模板文件中。

$ python3 app.py 
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

我们启动服务器。

$ curl http://127.0.0.1:5000/greet?name=Peter
<!doctype html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <title>Greeting</title>
</head>

<body>
    <p>
        Hello Peter
    </p>
</body>

</html>

我们使用curl工具连接到应用。 我们添加一个名称参数。

在本教程中,我们介绍了 Python Jinja 模块。

您可能也对以下相关教程感兴趣: PyMongo 教程Python 日志记录教程pyDAL 教程Python 教程

{% endraw %}

posted @ 2024-10-24 18:17  绝不原创的飞龙  阅读(8)  评论(0编辑  收藏  举报