C 和 Python 应用教程(三)
四、Python 中的 SQL
本章向读者介绍了如何从 Python 程序中访问和使用数据库语言“结构化查询语言”。将演示如何创建数据库文件。完成后,读者将能够创建数据库表。然后,将展示如何向表中插入数据,如何修改表中的数据,如何从表中删除数据,以及如何打印出表中保存的数据。
SQL 回顾
SQL(结构化查询语言)用于访问数据库。理解这一点最简单的方法是看一个数据库表的例子。下面显示了一个这样的表。
典型的数据库表包含为同一家公司工作的大量人员的信息。一个人的数据包含在一个“行”中这一行包含这个人的 ID、他们的姓、他们的年龄和他们的职位(职业)。
|编号
|
姓
|
最初的
|
性别
|
年龄
|
职业
| | --- | --- | --- | --- | --- | --- | | One hundred and twenty-three | 琼斯 | A | M | Thirty-seven | 会计师 | | One hundred and twenty-five | 锻工 | 稀有 | F | forty-two | 小时 | | One hundred and twenty-eight | 艾伦 | S | M | Twenty-eight | 秘书 | | One hundred and thirty-one | 布拉德利 | J | F | Twenty-six | 节目编排者 | | One hundred and thirty-two | 爱德华兹 | P | M | Forty-one | 节目编排者 | | One hundred and thirty-three | 国王 | B | F | Thirty-five | 软件工程师 | | One hundred and thirty-four | 价格 | C | M | Thirty-nine | 硬件工程师 | | One hundred and thirty-six | 罗伯茨 | M | F | fifty-two | 经理 | | One hundred and thirty-eight | 鼓励 | M | F | forty-four | 分析师 | | One hundred and thirty-nine | 香农河 | M | F | Twenty-four | 节目编排者 | | One hundred and forty-one | 刘易斯 | 稀有 | M | Twenty-seven | 接待员 |
我们将使用关系数据库管理系统 sqlite3 。这是在一个 C 库中,作为我们的 SQL 接口。
使用 Python 从命令行创建数据库。
键入“Python”
>>> import sqlite3
>>> conn = sqlite3.connect('bernard3.db')
这将在当前目录中创建数据库文件 bernard3.db。它的指针在“conn”中返回。
我们可以对数据库文件和它的一个表使用相同的名称。所以我们可以称他们为“人事”。
我们使用 SQL 来创建表。我们将把这个表称为“Personnel ”,因此这个表的 SQL 命令应该是
CREATE TABLE Personnel (id INT PRIMARY KEY, surname TEXT, initial TEXT, gender TEXT, age INT, occupation)
在这种情况下,ID 是唯一标识个人的“主键”。例如,如果两个人有相同的姓、首字母和性别,那么由于他们的 ID(主键)不同,这将唯一地标识他们。
我们将为每个人创建一行,为每个人创建一个单独的“INSERT”语句。我们表中的第一个人可以用下面的语句来定义
INSERT INTO Personnel VALUES (123, 'Jones', 'A', 'M', 37, 'Accountant')
其中 123 是 id,“琼斯”是姓氏,“A”是首字母,“M”是性别,“37”是年龄,“会计”是职业。
如果我们想要公司里所有程序员的名字,那么一个 SQL 语句会说
SELECT surname FROM Personnel WHERE occupation = 'Programmer'
我们可以使用“HAVING”选项来选择一个特定的组,在本例中是年龄大于 25 岁的人。这里,命令中的“*”表示“所有人”
SELECT * FROM Personnel GROUP BY age HAVING age > 25
我们可以使用“ORDER BY”选项选择特定的组。
以下示例从 Personnel 表中选择所有行,并按年龄降序排列:
SELECT * FROM Personnel ORDER BY age DESC
我们可以使用“更新”命令来修改行。id 为 123 的人的职业更改为“经理”,如下图所示:
UPDATE Personnel SET occupation = 'manager' WHERE id = 123
最后,我们可以使用“删除”命令删除一行,如下所示:
DELETE FROM Personnel WHERE id = 136;
我们将使用可免费下载的 sqlite3 标准软件。
Python 程序使用 import sqlite3,它使程序能够访问 sqlite3 库例程。
sqlite3.connect 打开数据库连接并返回连接对象。因此,如果我们有 conn = sqlite3 . connect(' personal . db '),我们可以使用 conn 来访问这些例程。在这里,我们使用 cur = conn.cursor()来设置 cur,然后 cur 将访问如下所示的 execute 命令:
cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)')
conn.close()关闭数据库连接。
看到这些基本的想法后,我们现在可以开始写程序了。
创建表格
如前所述,该程序创建“人员”表。
实际的数据库是文件Personnel.db。我们的数据库表被添加到其中。
清单 4-1 演示了这一点。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor() #open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
cur.execute('DROP TABLE IF EXISTS Personnel') # delete the table if it already exists
cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)') #create the table, specifying the items in each row
conn.close() # close the database connection
Listing 4-1pycretab.py
输出是
Opened database successfully
插入行的机制
为了插入一行(如前所述),我们设置了“INSERT INTO”命令。
在“INSERT INTO”命令中,当我们执行该命令时,我们有(?, ?, ?, ?, ?, ?)在命令的值部分之后。在此之后的括号部分,我们有将被替换到问号位置的值。因此
'INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(1, 'Jones', 'A', 'M', 23, 'Accountant'))
会产生
'INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (1, 'Jones', 'A', 'M', 23, 'Accountant'))
当要求用户输入要插入、更新或删除的数据时,这是一种有用的机制,此时值后面括号中的值就是用户输入的值。
前面的方法将数据“预设”到插入命令字符串中。以后,我们将有程序允许用户在程序运行时将 ID、姓名、首字母、性别、年龄和职业插入到程序中。
创建一个表格并插入两个预设行
该程序创建表格并插入两个预设行。然后,它从表中选择所有的行,这样我们就可以看到插入的行。我们使用命令‘DROP TABLE IF EXISTS Personnel’来确保如果我们试图创建的表已经存在,那么它将被删除。否则,我们的程序将失败,并显示错误“sqlite3”。OperationalError:表人员已经存在。如清单 4-2 所示。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
cur.execute('DROP TABLE IF EXISTS Personnel') # delete the table if it already exists
cur.execute('CREATE TABLE Personnel (id INTEGER PRIMARY KEY, name TEXT, initial TEXT, gender TEXT, age INTEGER, occup TEXT)') #create the table, specifying the items in each row
# Now Insert two rows into the table
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(1, 'Jones', 'A', 'M', 23, 'Accountant'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(2, 'Smith', 'J', 'M', 47, 'Salesman'))
print('Personnel:')
# Select everything contained in the table
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row) # print each row contained in the table
conn.commit() #commit these transaction so they can be seen by other programs
conn.close() #close the database connection
Listing 4-2pycretabins2.py
输出是
Opened database successfully
Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')
插入六个预设行
这个程序,如清单 4-3 所示,插入六个预置行。如果您在前面的示例之后运行这个程序,那么这六行应该被添加到表中。请注意,我们没有在下面的程序中创建表,因为这将删除前面插入的两行。你只需在程序中插入六条单独的 cur . execute(' INSERT INTO personal ')指令。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
# Now Insert six rows into the table
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(11, 'Jones', 'A', 'M', 23, 'Accountant'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(12, 'Smith', 'J', 'M', 47, 'Salesman'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(13, 'Zeiss', 'H', 'F', 38, 'Architect'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(14, 'Blaine', 'S', 'F', 28, 'SE'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor'))
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(16, 'Junkers', 'A', 'M', 59, 'Designer'))
print('Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel') # Select everything contained in the table
for row in cur:
print(row) # print each row contained in the table
conn.commit()#commit these transaction so they can be seen by other programs
conn.close() #close the database connection
Listing 4-3pyins6.py
输出是
Opened database successfully
People:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 47, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')
插入用户指定的行
现在我们已经有了包含行的数据库表,我们可能想要添加另一行(例如,如果公司刚刚招聘了一名新员工)。
我们现在有一个程序,它将插入一个单独的行,其 ID 和其他字段由用户指定。程序要求用户依次插入每个字段。如清单 4-4 所示。
#!/usr/bin/python
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
# User is asked to enter the name, ID, initial, gender, age and occupation
while True:
namein = input('Enter an name, or quit: ') # age stored in namein
if(namein == 'quit'): break #exit the while loop
idin = input('Enter ID: ') # id stored in 'idin'
initial = input('Enter initial: ') # initial stored in 'initial'
gender = input('Enter gender: ') # gender stored in 'gender'
agein = input('Enter age: ') # age stored in 'agein'
occup = input('Enter occupation: ') # occupation stored in 'occup'
# Now Insert row into the table using the values entered
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',(idin, namein, initial, gender, agein, occup))
break
print('Personnel:')
# Select everything contained in the table
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row) # print each row contained in the table
conn.commit()#commit these transaction so they can be seen by other programs
conn.close()#close the database connection
Listing 4-4pyuserins1.py
如果我们键入以下内容
Enter an name, or quit: Robinson
Enter ID: 21
Enter initial: C
Enter gender: F
Enter age: 31
Enter occupation: Engineer
我们得到
Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 47, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 47, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
更新一行
更新行,预设
在这个程序中,如清单 4-5 所示,我们更新表中的一行,例如,如果我们想改变他们的年龄或职位。要更新的值被编码到程序中。我们使用 UPDATE 命令并说 SET 来表示我们希望改变行中的哪一项。使用 ID 来指定我们希望更改的行是最安全的。在这里的例子中,我们使用了 name 来指定行,但是,正如您可能注意到的,因为表中有两个人同名,所以它更新了这两行。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
# Now update the row in the table
# we want to set the age for the person named Smith to be 24
try:
cur.execute("UPDATE Personnel SET age = 24 WHERE name = 'Smith'")
except Error as e:
print(e)
# Select everything contained in the table
cur.execute("SELECT * FROM Personnel")
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()
Listing 4-5pyup1.py
输出是
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 59, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
注意,在这里,我们有两个名为 Smith 的人,所以更新已经更改了这两行。这是你需要小心的事情。在这里,最安全的方法是使用 ID 而不是名称。
按用户更新行
在这个程序中,如清单 4-6 所示,我们更新了表中的一行。要更新的值由用户输入以更新年龄。
该程序使用 conn.total_changes 函数,该函数返回自连接以来对表的总更改。因此,如果只进行了一次更改(插入、更新或删除),并且函数 conn.total_changes 返回零,那么我们知道尝试的更改一定失败了。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
# we want to set the age for the person whose name is entered into 'namein' to be the age which is entered into 'agein'
#the following is a while loop which can only be exited by the user entering "quit".
while True:
namein = input('Enter an name, or quit: ')
if(namein == 'quit'): break
print(namein)
agein = input('Enter age: ')
# Using a while loop
tot0 = 0
# Now update the row in the table
try:
cur.execute("UPDATE Personnel SET age = ? WHERE name = ?", (agein,namein,))
except:
print('Error in Update')
#conn.total_changes returns total changes since connection
# by setting tot0 to 0 before this update then only this
# update is checked
tot = conn.total_changes
print(tot)
if tot == tot0:
print('Table not updated')
else
# Select everything contained in the table
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-6pyuserup1.py
输出是(假设我们输入勇克士手表作为姓名,38 作为年龄)
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 44, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 38, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
Enter a name, or quit:
插入和更新行
我们可以在同一个程序中进行插入和更新。在这种情况下,插入和更新都是在代码中预设的。这显示在清单 4-7 中。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
#insert the row, specifying the items in the row
cur.execute('INSERT INTO Personnel (id, name, initial, gender, age, occup) VALUES (?, ?, ?, ?, ?, ?)',
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber'))
#update a different row, specifying the changed item in the row
cur.execute("UPDATE Personnel SET age = 28 WHERE name = 'Smith'")
# Select everything contained in the table
cur.execute("SELECT * FROM Personnel ")
# print each row contained in the table
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-7pyinsup.py
输出是
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 28, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 28, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
选择一行
在这个程序中,我们从表中选择一行。选择中使用的值被编码到程序中。我们使用 SELECT 命令和 WHERE 指令来指定我们感兴趣的行。按年龄选择的命令是
SELECT * FROM Personnel WHERE age = 28
该程序如清单 4-8 所示。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
# Select one row contained in the table where the age is 28
# If we did not have LIMIT 1 then every row which had an age of 28 would be displayed
cur.execute("SELECT * FROM Personnel WHERE age = 28 LIMIT 1")
#print the row selected
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-8pysel1.py
输出是
(2, 'Smith', 'J', 'M', 28, 'Salesman')
选择用户输入的行
在这个程序中,如清单 4-9 所示,我们从表中选择一行。要在选择中使用的值由用户输入。用户想要找到被命名的人的年龄。找到时会显示此信息。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
while True:
namein = input('Enter an name, or quit: ')
if(namein == 'quit'): break
print(namein)
cur.execute('SELECT age FROM Personnel WHERE name = ? LIMIT 1', (namein, ))
(age, ) = cur.fetchone()
print(age)
break
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-9
pyusersel1.py
输出是(如果我们输入名称“Zeiss”)
Enter an name, or quit: Zeiss
Zeiss
38
按年龄降序选择
按年龄选择表中的所有行,并按年龄降序排序。这是在 SQL select 语句中完成的
SELECT * FROM Personnel ORDER BY age DESC
这将按年龄降序排列整个表。清单 4-10 显示了代码。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
cur.execute("SELECT * FROM Personnel ORDER BY age DESC")
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-10pyselorder.py
输出是
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(14, 'Blaine', 'S', 'F', 28, 'SE')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
用户输入的按年龄选择
这个程序,如清单 4-11 所示,从表中选择年龄大于用户指定值的人。这是通过使用 SELECT 命令中的“HAVING”来完成的
("SELECT * FROM Personnel GROUP BY age HAVING age > ?",(ageins,))
其中“年龄”是用户指定的年龄。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
ageins = input('Enter age: ')
print(ageins)
while True:
# Using a while loop
cur.execute("SELECT * FROM Personnel GROUP BY age HAVING age > ?",(ageins,))
for row in cur:
print(row)
break
conn.commit()#commit these transaction so they can be seen by other programs
cur.close()#close the database connection
Listing 4-11pyusersel1hav.py (user inputs age)
如果输入 24,则输出为
(14, 'Blaine', 'S', 'F', 28, 'SE')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
删除一行
这个程序,如清单 4-12 所示,从表中删除一行。用户输入要删除其行的人的姓名。在“删除一行”程序之后,您可以运行下一节中的“读取一个表”程序来检查它是否工作。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
namein = input('Enter an name, or quit: ')
cur.execute('DELETE FROM Personnel WHERE name = ?',(namein,))
print('Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
conn.close()#close the database connection
user enters Blaine
Listing 4-12pydel1.py
输出是
Personnel:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 38, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
阅读表格
这个程序,如清单 4-13 所示,读取并打印出表格中的所有行。这样做的代码行可以在本章的任何其他程序中使用。因此,如果您做了任何修改,或者插入或删除了任何行,用户可以检查更改是否有效。
import sqlite3
conn = sqlite3.connect('Personnel.db') # open connection to database file Personnel.db cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print ("Opened database successfully")
cur = conn.cursor()#open connection to 'cursor' which facilitates SQL
print(' Personnel:')
cur.execute('SELECT id, name, initial, gender, age, occup FROM Personnel')
for row in cur:
print(row)
conn.commit()#commit these transaction so they can be seen by other programs
conn.close()#close the database connection
Listing 4-13pyreadtab.py
输出是
Opened database successfully
People:
(1, 'Jones', 'A', 'M', 23, 'Accountant')
(2, 'Smith', 'J', 'M', 24, 'Salesman')
(11, 'Jones', 'A', 'M', 23, 'Accountant')
(12, 'Smith', 'J', 'M', 24, 'Salesman')
(13, 'Zeiss', 'H', 'F', 38, 'Architect')
(15, 'Postlethwaite', 'D', 'M', 63, 'Advisor')
(16, 'Junkers', 'A', 'M', 37, 'Designer')
(21, 'Robinson', 'C', 'F', 31, 'Engineer')
(25, 'Van der Kirchoff', 'I', 'M', 34, 'plumber')
摘要
本章演示了如何使用 Python 编程语言创建 SQL 数据库表,然后在表中插入、修改和删除行。它还展示了如何以不同的特定顺序显示表中的数据。这将使用户能够修改他们现有的程序以包含 SQL 访问,或者为 SQL 应用程序编写新的 Python 程序。
练习
-
创建两个表,其 SQL 等价如下:
创建人员表(id 整数、姓名文本、姓名首字母、性别文本、年龄整数、职业文本)
和
创建表格供应(id 整数、名称文本、地址文本、类型文本)
然后在第一个表格中插入六行,在第二个表格中插入四行。第二个表是公司列表。公司名称在“coname”中供应,他们供应的货物类型在“type”中。
-
修改 insert 程序, pyuserins1.py ,这样就可以在表中插入任意多的行。
五、嵌入式 Python
自 20 世纪 70 年代早期以来,C 编程语言就一直在运行,并且从那时起一直是计算机软件开发的核心。Python 语言较新,可以执行一些 C 语言不能执行的功能。因此,能够编写一个 C 程序并在其中嵌入一些 Python 代码是很有用的。这就是本章将要阐述的内容。
我们将看看可以合并到 C 程序中的以下两个级别的 Python 代码:
-
调用一个简单的 Python 字符串。
-
调用一个 Python 程序。
为了将这两个级别嵌入到我们的 C 程序中,我们必须初始化 Python 解释器。主函数调用是 Py_Initialize()。在 Python 序列的最后,我们调用 Py _ Finalize();。
要调用一个简单的字符串,我们使用 PyRun _ SimpleString。
为了运行一个 Python 程序,我们使用PyRun_SimpleFile.
Python 有 matplotlib 和 numpy,可以嵌入 C 程序。这些将在程序使用它们的章节中介绍。
这些列表将被标记为“列表 5-1 ”等。,关联的嵌入式 Python 程序将用扩展名“b”标记。所以在这里,它的标签应该是“清单 5-1b”。
基本机制
清单 5-1 展示了前面描述的简单字符串选项。Python 只是打印“嵌入的 Python 字符串”。
#include <stdio.h>
#include "include/Python.h"
int main()
{
Py_Initialize();
PyRun_SimpleString("print('Embedded Python string')");
Py_Finalize();
return 0;
}
Listing 5-1cpyth1.c
这个程序打印
嵌入的 Python 字符串
清单 5-2 展示了第二个嵌入选项。这里,我们调用 Python 程序 pyemb7.py。我们创建一个名为char filename[]的变量,它将保存要调用的文件名。我们定义变量 fp,它是文件指针。
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"
int main()
{
char filename[] = "pyemb7.py"; /* store the python file name */
FILE* fp; /* file pointer */
Py_Initialize();
fp = _Py_fopen(filename, "r"); /* store file pointer in fp */
PyRun_SimpleFile(fp, filename);/* call the python program */
Py_Finalize(); /* end the python link */
return 0;
}
Listing 5-2cpyth8.c
下面的清单 5-2b 是一个简单的 Python 程序,它被称为。
print('Embedded Python program here')
print('Hello to C program')
Listing 5-2bpyemb7.py
这个程序打印
Embedded Python program here
Hello to C program
我们现在可以进展到更现实的 Python 嵌入。
画一条 2D 线
接下来的 C 程序基本上和前面的一样,只是调用了不同的 Python 程序。清单 5-3 演示了这一点。
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"
int main()
{
char filename[] = "plot6a.py";
FILE* fp;
Py_Initialize();
fp = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp, filename);
Py_Finalize();
return 0;
}
Listing 5-3cpyth17a.c
这个 Python 程序(清单 5-3b )演示了 numpy 和 matplotlib 的使用。Numpy 是一组数值化的程序,matplotlib 与绘制图形有关。Python 程序绘制直线 y = x + 3。我们使用 numpy 函数 np.arange(0,10)设置 x 值。这会创建 0 到 10 之间的等间距 x 值。我们使用 y = x + 3 来计算每个 x 值的 y 值。
然后我们调用 matplotlib 函数 plt.plot(x,y)来绘制图形。
import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0,10) #return evenly spaced values between 0 and 10
y = x + 3 # formula to calculate y values for the x values given in the previous instruction
plt.title("Embedded ") #title of graph
plt.xlabel("x axis") #x axis label
plt.ylabel("y axis") #y axis label
plt.plot(x,y) #plot the graph
plt.show()
Listing 5-3bplot6a.py
这产生了如图 5-1 所示的图形。
图 5-1
y = x + 3 的基本直线
我们现在可以在图上画两条线了。
画两条 2D 线
C 和 Python 的这种结合展示了 Python 中 matplotlib 的一些灵活性。它在同一个图形上绘制两条曲线。清单 5-4 演示了这一点。
-
Listing 5-4bmp2aa.py
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"
int main()
{
char filename[] = "mp2aa.py";
FILE* fp;
Py_Initialize();
fp = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp, filename);
Py_Finalize();
return 0;
}
Listing 5-4cpyth29.c
这个程序(清单 5-4b )绘制了两个图形。一张图显示了女性考试分数的分布情况(百分比值),另一张图显示了男性考试分数的分布情况。
我们使用 list(range(0,100,10)) 函数为两个图形创建一组 x 值(标记)。
import matplotlib.pyplot as plt
# x values:
marks = list(range(0,100,10)) #marks (x values) in range 0 to 100 in units of 10
# y values:
male = [4, 7, 9, 17, 22, 25, 28, 18, 6, 2] # number of males within each range
female = [2, 5, 8, 13, 28, 25, 23, 20, 18, 12] # number of females within each range
# x axis label and y axis label
plt.xlabel('marks')
plt.ylabel('number of students')
#title of graph
plt.title('Comparison of male / female examination scores')
#plot points and adjoining lines for both male and female
#show a key to which line is male and which is female
plt.plot(marks, female, label="female")
plt.plot(marks, female, "ob") # ob means plot a circle character which is blue
plt.plot(marks, male, label="male")
plt.plot(marks, male, "or") # or means plot a circle character which is red
plt.legend()
plt.show()
该程序绘制出如图 5-2 所示的曲线。
图 5-2
男女考试成绩对比
这两个图表显示了男女学生的类似分布。这种一般形状称为“正态分布”
Matplotlib 还可以绘制标准三角曲线,如下所示。
绘制三角曲线
清单 5-5 中的下一个组合显示了标准的 matplotlib tan(x)函数。
-
Listing 5-5bmp5ae.py
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"
int main()
{
char filename[] = "mp5ae.py";
FILE* fp;
Py_Initialize();
fp = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp, filename);
Py_Finalize();
return 0;
}
Listing 5-5cpyth32.c
在这个程序(清单 5-5b )中,我们使用 np.arange(-2np.pi,2np.pi,0.1)给我们一组 x 值,它们是 pi 的倍数。因此,我们可以画出一个标准的三角函数。
import numpy as np
import matplotlib.pyplot as plt
# Choose evenly spaced x intervals
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
# plot y = tan(x)
plt.plot(x, np.tan(x))
# Set the range of the axes
plt.axis([-2*np.pi, 2*np.pi, -2, 2])
# Include a title
plt.title('y = tan(x)')
# Optional grid-lines
plt.grid()
plt.xlabel('x values')
plt.ylabel('y values')
# Show the graph
plt.show()
该程序绘制出如图 5-3 所示的曲线。
图 5-3
标准曲线 y = tan(x)
网格图是一个 matplotlib 选项。在正切三角曲线的情况下,包含网格是有用的,因为它显示了渐近线。
我们可以允许用户输入要绘制的数据点,如下例所示。
输入要绘制的数据
下一个例子有一个更重要的 C 程序。该程序计算数学值积矩相关系数。这是对图形中 x 值和 y 值之间关系的度量。用户输入 x 和 y 值。C 程序计算这些值的 PMCC,并将其写入文件pmccfcnt.bin,将 x 和 y 值写入文件pmccf.bin。Python 程序读取这两个文件,并创建一个显示(x,y)点和 PMCC 值的图形。如果 x 和 y 值之间的关系是一条正斜率的直线,则 PMCC 为+1。如果我们得到一条负梯度的直线,那么 PMCC 就是-1。如果这些点几乎在正的直线上,那么 PMCC 大约是 0.9568。这些点离直线越远,PMCC 离 1 就越远,例如 0.7453。
下面我们来看一个例子,我们正在研究一辆汽车的价值如何在 6 年内贬值。在图 5-4 中,x 是年数,y 是以千美元计的汽车价值。
图 5-4
汽车折旧的 x,y 点
其曲线图如图 5-5 所示。
图 5-5
汽车折旧图
Value ($1000)
汽车折旧的 PMCC 是 0.996961。
PMCC 的公式是
r = Sxy/(Sx *Sy)(1)
其中 Sx=√Sxx(2)
而 Sy=√SYY(3)
s【xx =【x】【2】——【x】【2】/n(4)
s【YY =【和——【y】/n(5)
sxy =xy-(∑x∑y)/n(6)
∑x 表示所有 x 值的总和。
∑y 表示所有 y 值的总和。
∑x 2 表示所有 x 值的平方,然后求和。
∑y 2 表示所有 y 值的平方,然后求和。
∑xy 的意思是将每个 x,y 对相乘,然后求和。
在我们的六个公式中使用这些值,我们得到
x= 2.5+3.0+3.5+4.0+4.5+5.0+5.5+6.0 = 34
和= 11.5+10.6+9.2+7.8+6.1+4.7+3.9+1.8 = 55.6
xy= 2.5 * 11.5+3.0 * 10.6+3.5 * 9.2+4.0 * 7.8+4.5 * 6.1+5.0 * 4.7+5.5 * 3.9+6.0 * 1.8
= 28.75 + 31.8 + 32.2 + 31.2 + 27.45 + 23.5 + 21.45 + 10.8
= 207.15
x【2】= 2.5+3.0+3.52+4
= 6.25 + 9 + 12.25 + 16 + 20.25 + 25 + 30.25 + 36
= 155
和【2】= 11.5+2+10.6+2+9.2+2+7
= 132.25 + 112.36 + 84.64 + 60.84 + 37.21 + 22.09 + 15.21 + 3.24
= 467.84
根据∑x 和∑y 的值,我们得到
= * x/8 = 34/8 = 4.25
= y/8 = 55.6/8 = 6.95
根据∑x 2 、∑y 2 和∑xy 的值,我们得到
s【xx =【x】——【x】/n
= 155–342/8 = 10.5
s【YY =【和——【y】/n
= 467.84–55.62/8 = 81.42
sxy =xy-(∑x∑y)/n
= 207.15 – 34*55.6 / 8 = –29.15
所以我们现在可以写
Sx=√Sxx =3.24037
S 和=s【YY =9.0233
对 PMCC 使用这些值
r = Sxy/(Sx *Sy)
=–29.15/(3.24037 * 9.0233)
=–0.996961
因此,汽车折旧问题的乘积矩相关系数的值是–0.996961。这非常接近于-1,这将是完美的负相关。
如果您不熟悉前面的术语,∑是希腊字母“sigma”因此,在下面的程序中,我们称∑x 为“sigmax ”,之前使用的其他术语也是如此。
在程序中,我们使用 sigmax,sigmay,sigmaxsquared,sigmaysquared,xbar,ybar,sigmaxy。
在下面的程序中,如清单 5-6 所示,要求用户输入 x,y 对中的数据点。运行此程序时,请输入以下几点:
/*product moment correlation coefficient */
#define _CRT_SECURE_NO_WARNINGS
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <math.h>
#include <conio.h>
#include "include/Python.h"
main()
{
double xpoints[10], ypoints[10];
double sigmax, sigmay, sigmaxsquared, sigmaysquared, xbar, ybar, sigmaxy;
double sxy, sxx, syy, sx, sy, r;
int i, points;
double fltcnt;
char filename[] = "searchpj3b.py"; /* python program to be called */
FILE* fp2;
FILE *fp;
FILE *fp3;
fp=fopen("pmccf2.bin","w"); /* file to store (x,y) values */
fp3=fopen("pmccfcnt2.bin","w"); /* file to PMCC value */
/* User enters number of points in scatter graph */
/* with a maximum of 10 */
printf("enter number of points (max 10 ) \n");
scanf("%d", &points);
if (points > 10)
{
/* User set number of points to be greater than 10 */
/* Flag an error */
printf("error - max of 10 points\n");
}
else
{
fprintf(fp3,"%d\n",points);
/* set store areas to zero */
sigmax = 0;
sigmay = 0;
sigmaxy = 0;
sigmaxsquared = 0;
sigmaysquared = 0;
/* User enters points for scatter graph */
for (i = 0;i < points;i++)
{
printf("enter point (x and y separated by space) \n");
scanf("%lf %lf", &xpoints[i], &ypoints[i]);
/* totals incremented by x and y points */
sigmax = sigmax + xpoints[i];
sigmay = sigmay + ypoints[i];
sigmaxy = sigmaxy + xpoints[i] * ypoints[i];
sigmaxsquared = sigmaxsquared + pow(xpoints[i], 2);
sigmaysquared = sigmaysquared + pow(ypoints[i], 2);
}
/*print points and write them to file */
printf("points are \n");
for (i = 0;i < points;i++)
{
printf(" \n");
printf("%lf %lf", xpoints[i], ypoints[i]);
fprintf(fp,"%lf\t%lf\n",xpoints[i], ypoints[i]);
}
printf(" \n");
fltcnt = points;
/* variables in PMCC formula calculated */
xbar = sigmax / fltcnt;
ybar = sigmay / fltcnt;
syy = (1 / fltcnt)*sigmaysquared - ybar * ybar;
sxx = (1 / fltcnt)*sigmaxsquared - xbar * xbar;
sx = sqrt(sxx);
sy = sqrt(syy);
sxy = (1 / fltcnt)*sigmaxy - xbar * ybar;
/* PMCC value calculated */
r = sxy / (sx*sy);
printf("r is %lf", r);
fprintf(fp3,"%lf\n",r);
}
fclose(fp);
fclose(fp3);
/* Call python program to print the graph */
Py_Initialize();
fp2 = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp2, filename);
Py_Finalize();
}
Listing 5-6pmccf3.c
x values y values
1.000000 2.000000
2.000000 3.000000
3.000000 5.000000
4.000000 9.000000
5.000000 10.000000
6.000000 13.000000
Python 程序(清单 5-6b )读取数据文件并创建图表。
import matplotlib.pyplot as plt
import numpy as np
#if there are 8 entered coordinates then this will be the arrays
#xvals = [0,1,2,3,4,5,6,7]
#yvals = [0,1,2,3,4,5,6,7]
#xvals = [0]*8
#yvals = [0]*8
# Read data from pmccf.bin file
y = np.loadtxt("pmccf.bin")
print("Data read from pmccf.bin")
print("y = ",y)
# Read data from pmccfcnt.bin file
z = np.loadtxt("pmccfcnt.bin")
print("Data read from pmccfcnt.bin")
print("z = ",z)
a,b = z # a is no. of coords entered, b is PMCC value
#zint is the number of coordinates entered
zint = int(a)
print("number of coordinates entered = ", zint)
print("PMCC = ", b)
float_b = b;
string_b = str(float_b)
# Set up the arrays for the graph
xvals = [0]*zint #length of array is num. of coords entered
yvals = [0]*zint #length of array is num. of coords entered
# set up the x and y arrays from the values entered
for x in range(zint):
a,b = y[x]
xvals[x] = a
yvals[x] = b
# Print the x and y values to the user
print("xvals = ",xvals)
print("yvals = ",yvals)
# Display the graph
plt.xlabel('x values')
plt.ylabel('y values')
plt.title('PMCC Test Graph')
plt.text(1.0, 10, 'PMCC =')
plt.text(2.0, 10, string_b)
plt.plot(xvals, yvals, "ob")
plt.show()
Listing 5-6bsearchpj3b.py
这将输出到命令行
enter number of points (max 10 )
6
enter point (x and y separated by space)
1 2
enter point (x and y separated by space)
2 3
enter point (x and y separated by space)
3 5
enter point (x and y separated by space)
4 9
enter point (x and y separated by space)
5 10
enter point (x and y separated by space)
6 13
points are
1.000000 2.000000
2.000000 3.000000
3.000000 5.000000
4.000000 9.000000
5.000000 10.000000
6.000000 13.000000
r is 0.986227
Data read from pmccf.bin
y = [[ 1\. 2.]
[ 2\. 3.]
[ 3\. 5.]
[ 4\. 9.]
[ 5\. 10.]
[ 6\. 13.]]
Data read from pmccfcnt.bin
z = [6\. 0.986227]
number of coordinates entered = 6
PMCC = 0.986227
xvals = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
yvals = [2.0, 3.0, 5.0, 9.0, 10.0, 13.0]
这产生了如图 5-6 所示的图形。
图 5-6
PMCC 测试图
在这种情况下,计算的 PMCC 是 0.986227,非常接近+1,这将是完美的正相关,其中所有点都正好位于一条直线上。
接下来,我们将研究一种寻找物体质心的机制。
2D 质心图
这里,我们想找出由 2D U 形曲线(y = x**2)和直线 y = 4 围成的区域的质心在哪里。如果你做了一个如图 5-7 所示的形状,你用木头或熟石膏做了一个实心的形状,然后把它平放,你应该可以把手指放在重心点的下方,让它在手指上保持平衡。
如果你看下面的图,你可以看到它是关于 x = 0 的线对称的,所以你会期望质心位于那条线上。同样,看着这个物体,你可以看到在形状的顶部有更多的物质,所以你会认为质心在 x = 0 线的上部。我们可以通过使用随机数发生器机制找到它的确切位置。
计算机上的随机数生成器可以生成我们指定范围内的随机数。这里,我们希望我们的数字在曲线 y = x2 之上,在直线 y = 4 之下。我们可以生成 x =–2 和 x = +2 之间的数字以及 y = 0 和 y = 4 之间的 y 值。这给了我们围绕曲线的正方形内的数字。这是 x =–2 和 x = +2 以及 y =0 和 y = 4 之间的平方。当我们为一个点生成 x 和 y 值时,我们需要检查该点是否位于曲线上方,或者用数学术语来说,y > x2。我们为了 3500 点一直这样做。如果 x 和 y 值在曲线内,那么我们把 x 值加在一起,把 y 值加在一起。这样做 3500 次后,我们把 x 总数除以 3500,y 总数除以 3500。结果就是质心的坐标。我们把所有的 3500 个点写入一个输出文件,还有计算出的质心。
然后,我们将控制权交给 Python 程序,该程序读取这两个文件,并将生成的所有点绘制在一个图形上。每个点都是蓝色的,质心点是红色的。如图 5-7 所示。
图 5-7
我们想要找到质心的形状
C 程序是 cofm5a.c(清单 5-7 ),内嵌的 Python 程序是 searchpj4.py(清单 5-7b )。
#In this program "Centre of Mass" is abbreviated to cofm.
import matplotlib.pyplot as plt
import numpy as np
fhand = open('cofm5a.bin','r') #file of (x,y) values created by Cofm5a.c
count = 0
#if there are 8 entered coordinates then this will be the arrays
#xvals = [0,1,2,3,4,5,6,7]
#yvals = [0,1,2,3,4,5,6,7]
#xvals = [0]*8
#yvals = [0]*8
y = np.loadtxt("cofm5a.bin") # read x,y values
print("Data read from cofm5a.bin")
print(y)
z = np.loadtxt("cofm5acnt.bin") # read count of points, xcofm and ycofm
print("Data read from cofm5acnt.bin")
print(z)
a,p,j = z # split the 3 z values into separate variables
zint = int(a)
print("zint is " ,zint) # total number of points
string_p = str(p)
print("string_p is ",string_p ) # x value of c of m
string_j = str(j)
print("string_j is ",string_j ) # y value of c of m
xvals = [0]*zint
yvals = [0]*zint
# store the x and y coordinates into xvals[] and yvals[]
for x in range(zint-1):
a,b = y[x]
xvals[x] = a
yvals[x] = b
plt.xlabel('x values')
plt.ylabel('y values')
plt.title(' CofM Graph (red spot is Centre of Mass)')
plt.plot(xvals, yvals, "ob")
plt.plot(p, j, "or")
plt.show()
Listing 5-7bsearchpj4.py
/* cofm5a.c
Centre of Mass Calculation.
In this program “Centre of Mass” is abbreviated to cofm.
Calculates c of m for 2D shape y = x^² between y = 0 and y = 4 */
#define _CRT_SECURE_NO_WARNINGS
#define PY_SSIZE_T_CLEAN
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <conio.h>
#include "include/Python.h"
double randfunc();
main()
{
int I,outcount;
float area,total,count;
FILE *fptr;
char filename[] = "searchpj4.py"; /* python program name */
FILE* fp2;
FILE *fp3;
time_t t;
/* Local Arrays */
double x, y,xout[3500],yout[3500],xcofm,ycofm;
/* file cofm5a.bin contains all points inside the curve */
/* file cofm5acnt.bin contains total number of points inside the curve and the x and y position of the centre of mass*/
fptr=fopen("cofm5a.bin","w");
fp3=fopen("cofm5acnt.bin","w");
/* Initializes random number generator */
srand((unsigned) time(&t));
/* clears arrays to zero */
for( I = 0; I<3500;I++)
{
xout[I] = 0.0;
yout[I] = 0.0;
}
/* set x and y cofm accumulators to zero */
xcofm=0.0;
ycofm=0.0;
total = 0.0;
count = 0.0;
outcount = 0;
for( I = 1;I<= 3500;I++)
{
/* get x values between -2 and +2 */
/* get y values between 0 and +4 */
x = randfunc()*4.0-2.0;
y = randfunc()*4.0;
/* If the generated x and y values are above */
/* the curve y=x² then add 1 to count */
/* and update the x and y cofm values */
if(y>pow(x,2))
{
xcofm=xcofm+x;
ycofm=ycofm+y;
total = total+1;
outcount = outcount +1;
xout[outcount] = x;
yout[outcount] = y;
}
count = count+1;
}
area=(total/count)*16;/* area is part of the square which is 4x4 or 16 sq units */
printf("total is %f count is %f\n",total,count);
xcofm=xcofm/total;
ycofm=ycofm/total;
printf("area is %lf\n",area);
printf("cofm is %lf,%lf",xcofm,ycofm);
/* Plot the data */
if(outcount >= 2700)
outcount = 2700;
fprintf(fp3,"%d\t%lf\t%lf\n",outcount,xcofm,ycofm);
for(I = 1; I<=outcount-1;I++)
fprintf(fptr,"%lf %lf\n",xout[I],yout[I]);
fclose(fptr);
fclose(fp3);
/* Call python program to read the file and produce the diagram showing the position of the centre of mass */
Py_Initialize();
fp2 = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp2, filename);
Py_Finalize();
}
double randfunc()
{
/* get a random number 0 to 1 */
double ans;
ans=rand()%1000;
ans=ans/1000;
return ans; /* return the random number to the caller */
}
Listing 5-7cofm5a.c
这段代码向命令行输出以下内容:
total is 2331.000000 count is 3500.000000
area is 10.656000
cofm is 0.006847,2.359818Data read from cofm5a.bin
[[-1.548 3.268]
[-0.872 2.716]
[-0.16 0.068]
...
[ 0.136 2.972]
[ 0.18 0.172]
[ 1.484 3.744]]
Data read from cofm5acnt.bin
[2.331000e+03 6.847000e-03 2.359818e+00]
zint is 2331
string_p is 0.006847
string_j is 2.359818
这段代码产生了下图,如图 5-8 所示。
图 5-8
质心图
质心用红色的点表示。你可以猜测这可能是正确的,因为物体的形状关于 x = 0 的线是对称的,光斑看起来在那条线上,在形状的顶部有更多的蓝点,而红点在形状的顶部。
这个嵌入式 Python 程序展示了 C 和 Python 的优点,以及嵌入式技术的实用性。
我们现在来看看直方图,因为它们在经济学的许多领域都很有用。
直方图
直方图是一种表示数据分布的图形方式。它们在外观上类似于条形图,但它们将频率密度显示为分布而不是频率。
图表中的条形被称为“条柱”
以下程序显示了显示数字分布的直方图:
1,2,3,4,45,66,67,68,69,70,84.88,91,94
该程序将这些值分成十个组。我们在程序中指定需要 10 个面元,这个值用于调用绘制直方图的函数 plt.hist。对该函数调用的回复告诉您每个箱子中有多少物品。然后我们可以把它打印出来,我们看到值是
4\. 0\. 0\. 0\. 1\. 0\. 1\. 4\. 0\. 3.
因此,第一个容器中有四个项目(前面值列表中的 1,2,3,4),接下来的三个容器中没有项目,下一个容器中有一个项目,依此类推。
在清单 5-8 中,我们绘制了直方图,如图 5-9 所示。
import matplotlib.pyplot as plt
values = [1,2,3,4,45,66,67,68,69,70,84.88,91,94]
# draw a histogram with 10 bins of the `values’ data
number_of_bins = 10
n = plt.hist(values, number_of_bins, facecolor='blue')
print(n[0]) # counts in each bin
# plot the histogram
plt.title('test histogram')
plt.xlabel('preset numbers between 1 and 100')
plt.ylabel('Number in each bin')
plt.show()
This outputs
[4\. 0\. 0\. 0\. 1\. 0\. 1\. 4\. 0\. 3.]
Listing 5-8plot2b.py
图 5-9
测试直方图
本章末尾的练习中使用了这种直方图机制。
我们的最后一个例子展示了如何将一张图片导入我们的程序。
导入图片
Python 有“Image”、“ImageDraw”、“ImageFont”,可以从“PIL”导入,嵌入 C 程序。Python 程序可以使用它们来读取文件中的照片图像并打印出来。然后我们可以用文字覆盖一些图片。清单 5-9 演示了这一点。
#define PY_SSIZE_T_CLEAN
#include <stdio.h>
#include <conio.h>
#include "include/Python.h"
int main()
{
char filename[] = "embim9.py";
FILE* fp;
Py_Initialize();
fp = _Py_fopen(filename, "r");
PyRun_SimpleFile(fp, filename);
Py_Finalize();
return 0;
}
Listing 5-9cpythim1.c
Python 程序(清单 5-9b )读取包含罗马图片的文件并绘制图像。然后,它会在图片顶部写下标题“罗马 2016”。如图 5-10 所示。
from PIL import Image, ImageDraw, ImageFont
#Open image using Image module
im = Image.open("5Rome.jpg") #photograph of Rome
myFont = ImageFont.truetype(r'C:\Users\System-Pc\Desktop\arial.ttf', 80)
#The 'r' character before the path, in the code above, is necessary if the path contains backslashes, #as the backslash could be interpreted as an escape character.
d1 = ImageDraw.Draw(im)
# Print text "Rome 2016" at the top of the picture
d1.text((28, 36), "Rome 2016", font=myFont, fill=(255, 0, 0))
#Show final Image
im.show()
Listing 5-9bembim9.py
图 5-10
导入的图片
摘要
本章演示了如何将 Python 代码和完整的 Python 程序嵌入到 C 程序中。我们已经展示了,如果您需要执行 Python 比 c 语言更能胜任的任务,这是多么有价值。
锻炼
- 编写一个 C 程序,读入学生在一次考试中获得的 20 个用户输入的分数(0 到 100)。将这 20 个标记写入文件。编写一个 Python 程序来读取该文件,并从这 20 个值中创建一个直方图。从 C 程序调用 Python 程序。
六、套接字
套接字系统允许两个或多个程序通过互联网相互通信。如果需要,可以在程序之间双向发送消息。文件可以被传输,我们可以让一个套接字同时与其他几个套接字通信(这被称为“多线程”)。
近距离观察插座
两个套接字使用预定义的序列进行通信的术语称为“握手”这些预定义的序列被称为“协议”服务器和客户端都必须遵守相同的协议;否则,它们不会起作用。这有点像两个说不同语言的人,都不知道对方的语言。他们的交流很快就会中断。
一个相当常用的协议是 TCP/IP,它是“传输控制协议/互联网协议”的缩写该协议在互联网上使用。每台使用互联网的设备都有自己的“IP 地址”这是一个唯一的地址。您可以使用命令行工具“ipconfig”找到您使用的计算机的 IP 地址。IP 地址具有相同的格式。一个地址可能是“123.456.7.89”。地址总是具有相同的模式,由句号(句点)分隔的四个数字组成。这种类型的 IP 地址称为 IPv4,地址为 32 位。有一个新版本的 IP 地址叫做 IPv6,它的地址是 128 位。这里我们将使用 IPv4。
Python 中的 socket 系统是使用我们熟悉的“ import 指令来访问的。在这种情况下,我们使用
import socket
使用套接字系统的程序被分为“服务器或“客户端一般来说,服务器负责通信,客户端要求连接到服务器。一旦建立了连接,消息就可以从服务器发送到客户机,也可以从客户机发送到服务器。
图 6-1 显示了服务器和客户端的主要代码调用。
图 6-1
服务器-客户机机制
在每一端,程序发出“ socket 命令,启动程序中的套接字机制。
在服务器端,下一个命令是“ bind ”。这就建立了作为服务器发布“绑定”的程序。IP 地址和端口号在 bind 命令中给出。
服务器的下一个命令是“ listen ”。在这里,服务器等待来自客户端程序的连接命令。
在客户端,在初始的“ socket 命令之后,它发出“ connect 命令来与服务器建立连接。然后它调用“ connect 连接到特定的服务器(由 IP 地址和端口号标识)。
服务器现在发出一个“ accept 命令来接受来自客户端的连接命令。
既然连接已经建立,服务器和客户机就可以相互发送和接收消息了。
当服务器和客户端之间的所有业务完成后,客户端向服务器发送“ close 命令,服务器和客户端之间的连接结束。
通过互联网发送的信息通常使用 UTF-8。
UTF-8 是 Unicode 的一部分,Unicode 是一种国际标准,用于为每个字符分配一个唯一的编号。它类似于 ASCII。它使用 1、2、3 或 4 个字节进行编码(通常只有 1 个字节)。
消息中的数据通常是字符串格式,使用。encode()命令。如果服务器程序将它发送给客户机程序,那么客户机将使用。解码()。
我们现在可以看看不同类型的插座。
基本客户端-服务器
这是一个基本的单向的,服务器和客户端的组合。如上图所述,有一个基本的连接和通信。服务器启动并等待来自客户端的连接。客户端启动并连接到服务器。服务器向客户端发送消息。客户端接收并打印这条消息。则连接终止。
如果您的客户机和服务器在同一台机器上,那么您可以使用命令“gethostname”来获取它的标识符,然后使用它的端口号作为它的完整地址。如果你的服务器和客户端在不同的机器上,那么你不能使用“gethostame”,但是你必须使用它们的 IP 地址。
这里,我们的服务器和客户机在同一台机器上,所以我们可以使用“gethostame”。如果我们想找到我们的 IP 地址,我们可以使用命令 socket.gethostbyname(hostname)。
列表 6-1 是服务器。
# socket server
import socket # load socket module
skt = socket.socket() # Create a socket (refer to it as skt in this program)
hostname = socket.gethostname() # Get local host machine name
print(hostname) #print the host machine name
port = 12357 # Reserve a port (same as client port)
skt.bind((hostname, port)) # Bind the host name and port (establish server status)
skt.listen(5) # wait for a client to connect.
while True:
con, accaddr = skt.accept() # Accept connection with client.
# 'con' is the open connection between the server and client, 'accaddr' is the IP address and port number
print('con is ', con) #print values to the user
print('accaddr is ', accaddr) #print values to the user
print('Received connection from', accaddr) #print values to the user
message = "Got your connection"
con.send(message.encode()) #send message to client to confirm connect
con.close() # Close connection
break
The server outputs the following:
user-PC
con is <socket.socket fd=448, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('123.456.7.89, 12357), raddr=(123.456.7.89', 63730)>
accaddr is ('123.456.7.89', 63730)
Received connection from ('123.456.7.89', 63730)
(where the "123.456.7.89" characters are the IP addresses).
Listing 6-1socser10cx.py
第二个程序,清单 6-2 ,是客户端。
# socket client
import socket # load socket module
skt = socket.socket() # Create a socket
hostname = socket.gethostname() # Get local machine name
port = 12357 # Reserve a port (must be same as server)
skt.connect((hostname, port)) # connect to the server
data = skt.recv(1024) # receive data from server
print('data is ',data.decode()) #print the line of data received
skt.close() # Close connection
Listing 6-2soccli10bx.py
客户端输出以下内容:
数据得到了你的连接
这显示了一个基本的套接字操作。我们现在来看看套接字之间的文件传输。
服务器-客户端对发送-接收文件
以下服务器-客户端对发送/接收文件。
服务器读取文件“pjfile1.txt”的每一行,并将每一行分别发送给客户端。
“pjfile1.txt”包含
This is my line
second line
third line
fourth line
last line
客户端读取从服务器收到的每一行,打印出来,并写入输出文件“pjrecfile”。
清单 6-3 显示了文件发送的服务器。
# send file to client
import socket # Import socket module
port = 3000 # Reserve a port for your service.
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
s.bind((host, port)) # Bind to the port
s.listen(5) # Now wait for client connection.
print ('Server running')
while True:
conn, addr = s.accept() # Establish connection with client.
print ('Got connection from', addr)
data = conn.recv(1024) #receive connection from client
print('Server received', bytes.decode(data)) #print out the data received
filename='pjfile1.txt' #the name of the file to be opened
file = open(filename,'rb') #open the file we wish to send
# read each line in the file and send it to the client
line = file.read(1024)
while (line):
conn.send(line) #send the line to the client
print('Server received ',bytes.decode(line)) #tell user the line that has been received by client
line = file.read(1024) #read the next line
file.close() #finished sending so close the file
print('Finished sending')
conn.send(str.encode('Thank you for connecting')) #send final message to client
conn.close() #close the connection
break
Listing 6-3socserfile.py
连接后,服务器输出
Server received Client connected
Sent This is my line
second line
third line
fourth line
last line
Finished sending
清单 6-4 中显示的下一个程序是读取和打印文件的相关客户端程序。然后,它创建一个输出文件,并将读取的文件写入其中。
# Socket Client Program
# receive file from server
# and write it to a new file
import socket # Import socket module
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 3000 # Set the port for your service.
s.connect((host, port)) # connect to server
s.send(str.encode("Client connected")) #send connect confirmation to user
# open the output file (pjrecfile)
with open('pjrecfile', 'wb') as file:
print ('file opened')
while True:
print('receiving data from server')
data = s.recv(1024) # receive each line of data from server (at most 1024 bytes)
print('data is ',bytes.decode(data)) #print the line of data received
if not data:
break
# write data to the file
file.write(data)
file.close() # close the output file
print('Received file from server')
print('Written to output file pjrecfile')
s.close() #close the connection
print('connection closed')
Listing 6-4socclifile.py
连接后,客户端输出
file opened
receiving data from server
data is This is my line
second line
third line
fourth line
last line
receiving data from server
data is Thank you for connecting
receiving data from server
data is
Received file from server
Written to output file pjrecfile
connection closed
pjrecfile 包含
This is my line
second line
third line
fourth line
last line
Thank you for connecting
这展示了一种使用套接字进行文件传输的方法。到目前为止,我们只看到了一个服务器与一个客户机通信。我们现在将对此进行扩展。
线程程序
套接字的线程系统可以让几个客户端程序同时连接到一个服务器。客户端可以连接和断开任意多次。服务器循环等待客户端连接。清单 6-5 显示了线程服务器的代码。
import socket # Import socket module
from _thread import * # thread software
import os
mypid = os.getpid()# get Process ID (pid)for this program
print('Server My pid is', mypid) #print the pid in case the user wants to 'taskkill' this program
ServerSocket = socket.socket()
host = socket.gethostname() # Get local machine name
port = 1234
ThreadCount = 0 #count of how many clients have connected
ServerSocket.bind((host, port))
print('Waiting for Client connect')
ServerSocket.listen(5) #wait for a client connection
# Function for each connected client
def threadcli (cliconn):
cliconn.sendall(str.encode('Connect'))
while True:
data = cliconn.recv(2048) #receive message from client
reply = 'Server replies: ' + data.decode() #set up reply
if not data:
break
cliconn.sendall(str.encode(reply)) #send the reply to the client
cliconn.close()
# wait for a connection from a new client
while True:
Cli, addr = ServerSocket.accept()
print('Connected to: ' + addr [0] + ':' + str(addr [1]))
print(Cli) # show server and client addresses
start_new_thread(threadcli, (Cli, )) #function imported from '_thread'
# This calls local function 'threadcli'
# Each threaded client stays within its own
# 'threadcli' function
ThreadCount += 1 #add 1 to number of connected clients
print('Thread Number: ' + str(ThreadCount))
ServerSocket.close()
Listing 6-5socserthreadgx2.py
连接后,服务器输出
Server My pid is 6296
Waiting for Client connection
Connected to: ..................................(IP address and Port number)
Thread Number: 1
第二个程序是客户端。当客户端想要结束连接时,用户输入“windup ”,连接就会关闭。清单 6-6 显示了线程客户端的代码。
import socket
import os
mypid = os.getpid() # get Process ID (pid)for this program
print('Client My pid is', mypid) #print the pid
ClientSocket = socket.socket()
host = socket.gethostname() # Get local machine name
port = 1234
print('Waiting for connection')
ClientSocket.connect((host, port)) #connect to Server
Response = ClientSocket.recv(1024)
print(ClientSocket) # show server and client addresses
while True:
Input = input('Enter message: ') #ask user to enter their message
ClientSocket.send(str.encode(Input)) #send message to socket
Response = ClientSocket.recv(1024) #get response from server
if( Response.decode()) =='Server replies: windup':
# if client wants to disconnect from server, the user types 'windup'
break
print(Response.decode())
ClientSocket.close() #close the client
Listing 6-6socclithreadgx2.py
连接后,客户端输出
Client My pid is 2248
Waiting for connection
Enter message: hello from client1
Server replies: hello from client1
Enter message: windup
上面显示了多个客户端连接到一台服务器。由于客户端可以连接、断开连接,然后重新连接,这可能会导致冗余服务器出现问题。我们现在来看看这个。
关闭线程服务器
大多数套接字客户端和服务器可以定期关闭。但是,在线程套接字的情况下,服务器可能会无限期地运行,因为许多客户端可以在一段时间内连接和断开。
如果我们确定所有的客户端都已经与服务器断开连接,并且我们想要关闭服务器,我们可以使用命令行指令“tasklist”和“taskkill”。
“tasklist”命令为我们提供了当前正在运行的任务列表。一个例子如下所示:
C:\Users\user\AppData\Local\Programs\Python\Python37>tasklist
Image Name PID Session Name Session# Memory Usage
=================== ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 36 K
Registry 100 Services 0 51,384 K
smss.exe 352 Services 0 648 K
csrss.exe 516 Services 0 2,744 K
wininit.exe 628 Services 0 3,852 K
services.exe 700 Services 0 6,616 K
lsass.exe 708 Services 0 14,688 K
svchost.exe 904 Services 0 22,944 K
fontdrvhost.exe 932 Services 0 1,568 K
svchost.exe 68 Services 0 14,284 K
cmd.exe 12004 Console 3 5,044 K
conhost.exe 9936 Console 3 19,564 K
UserOOBEBroker.exe 11604 Console 3 9,040 K
notepad.exe 1448 Console 3 44,144 K
notepad.exe 10452 Console 3 41,768 K
python.exe 6576 Console 3 10,596 K
cmd.exe 7104 Console 3 5,048 K
conhost.exe 1332 Console 3 19,580 K
cmd.exe 10344 Console 3 5,284 K
conhost.exe 6120 Console 3 19,512 K
Microsoft.Photos.exe 772 Console 3 8,356 K
RuntimeBroker.exe 12072 Console 3 31,176 K
tasklist.exe 4944 Console 3 9,836 K
C:\Users\user\AppData\Local\Programs\Python\Python37>
我们看到我们的 python.exe 计划在这个列表中。我们可以使用“taskkill”关闭它,如下所示:
C:\Users\user\AppData\Local\Programs\Python\Python37> taskkill /F /IM python.exe
SUCCESS: The process "python.exe" with PID 6576 has been terminated.
C:\Users\user\AppData\Local\Programs\Python\Python37>
OR WE CAN USE THE PID TO TERMINATE IT'
如果有多个 python.exe 在运行,我们需要确保终止的是正确的一个。每个正在运行的程序都有一个唯一的进程 ID 或 PID。我们可以使用 pid 来确保终止正确的那个。这里,我们展示了一个包含两个 python.exe 程序的任务列表。
C:\Users\user\AppData\Local\Programs\Python\Python37>tasklist
Image Name PID Session Name Session# Memory Usage
=================== ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 36 K
Registry 100 Services 0 52,328 K
smss.exe 352 Services 0 648 K
csrss.exe 516 Services 0 2,752 K
wininit.exe 628 Services 0 3,852 K
services.exe 700 Services 0 6,600 K
lsass.exe 708 Services 0 14,768 K
svchost.exe 904 Services 0 22,944 K
fontdrvhost.exe 932 Services 0 1,568 K
svchost.exe 68 Services 0 14,268 K
svchost.exe 1056 Services 0 19,188 K
svchost.exe 1084 Services 0 25,116 K
svchost.exe 1120 Services 0 23,404 K
svchost.exe 1288 Services 0 27,984 K
svchost.exe 1344 Services 0 66,208 K
notepad.exe 1448 Console 3 44,144 K
notepad.exe 10452 Console 3 41,800 K
cmd.exe 7104 Console 3 5,052 K
conhost.exe 1332 Console 3 19,620 K
cmd.exe 10344 Console 3 5,284 K
conhost.exe 6120 Console 3 19,956 K
Microsoft.Photos.exe 772 Console 3 8,356 K
RuntimeBroker.exe 12072 Console 3 31,196 K
svchost.exe 12704 Services 0 9,592 K
audiodg.exe 4716 Services 0 11,748 K
smartscreen.exe 12000 Console 3 22,928 K
python.exe 5492 Console 3 10,548 K
python.exe 16648 Console 6 11,100 K
tasklist.exe 2448 Console 3 9,824 K
If we know that it is the one with pid of 5492, we can type
C:\Users\user\AppData\Local\Programs\Python\Python37>taskkill /F /PID 5492
并接收
SUCCESS: The process with PID 5492 has been terminated.
C:\Users\user\AppData\Local\Programs\Python\Python37>
如果你有两个或更多的 python.exe 程序在运行(每个都有不同的 pid),你可能不知道该杀哪个。如果我们知道我们可能想从命令行终止我们的进程,我们能做的就是让程序在启动时打印出它的 pid。它可以使用包含在操作系统库中的 getpid() Python 指令找到它的 pid。
因此,在我们的计划中,我们将包括
import os
mypid = os.getpid()
print('My pid is', mypid)
因此,指令 os.getpid()会返回变量“mypid”中的程序 pid,然后程序会将它打印出来。然后,用户可以像前面一样使用相关 pid 来使用“taskkill”命令,例如:
C:\Users\user\AppData\Local\Programs\Python\Python37>python Socclithreadg.py
My pid is 4120
Waiting for connection
<socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('123.456.7.89'), raddr=('123.456.7.89')>
Enter message:
然后在另一个窗口,我们将使用 taskkill
C:\Users\user\AppData\Local\Programs\Python\Python37>taskkill /F /pid 4120 /T
并接收
SUCCESS: The process with PID 4120 (child process of PID 16520) has been terminated.
C:\Users\user\AppData\Local\Programs\Python\Python37>
This section has shown how to close a server. This mechanism should only be used when necessary. We will now look at “chat” programs.
聊天程序
聊天套接字程序有双向发送和接收。服务器仍然启动连接过程,但是对话是双向的(因此称为“聊天”)。
清单 6-7 中显示的第一个程序是服务器。
# Server Side Script
# Socket Server Program
import time, socket, sys
server_port = 1000
server_socket = socket.socket()
host_name = socket.gethostname()
server_socket.bind((host_name ,server_port))
server_socket.listen(1) #look for client connect
print ("Server is loaded")
connection_socket, address = server_socket.accept() # accept client connect
while True:
sentence = connection_socket.recv(2048).decode() #receive incoming message
print('>> ',sentence) # print the message to the user
message = input(">> ") #ask the user to input a reply
connection_socket.send(message.encode()) #send reply
if(message == 'windup'):
connection_socket.close() # a 'windup' message means the user wants to disconnect
break
Listing 6-7socsert2x.py
来自服务器的输出(检查客户端输出以查看双向聊天):
Server is loaded
>> hello from client
>> hello from server
>> windup
>> windup
清单 6-8 中显示的第二个程序是客户端。当客户想要结束聊天时,用户输入“结束”,聊天就结束了。
# Client Side Script
# Socket Client Program
import time, socket, sys
server_name = socket.gethostname()
server_port = 1000
client_socket = socket.socket()
host_name = socket.gethostname()
client_socket.connect((server_name,server_port)) #connect to the server
while True:
sentence = input(">> ") #input your message
client_socket.send(sentence.encode())#send your message
message = client_socket.recv(2048) #receive reply
print (">> ", message.decode()) #print the reply
if(sentence == 'windup'):
client_socket.close() # a 'windup' command means the user wants to disconnect
break
Listing 6-8socclit2x.py
来自客户端的输出(检查服务器输出以查看双向聊天):
>> hello from client
>> hello from server
>> windup
>> windup
This has demonstrated two-way send and receive sockets.
The chapter has shown the fundamentals of sockets and the variety of types of communication we can use sockets for.
摘要
本章说明了套接字服务器和客户机如何交互,以及它们如何在不同的组合中使用。
锻炼
- 为服务器和客户端编写聊天程序。然后再写两个,与前两个的唯一区别是端口号。在不同的窗口上运行所有四个程序。检查具有相同端口号的服务器-客户端对是否可以相互聊天(即,具有一个端口号的服务器不应该与具有不同端口号的客户端聊天)。