第九天 項(xiàng)目實(shí)戰(zhàn)練習(xí)(4)+數(shù)據(jù)庫編程
今天計(jì)劃繼續(xù)坦克大戰(zhàn)練習(xí)虫给,學(xué)習(xí)項(xiàng)目及練習(xí)源碼地址:
GitHub源碼
坦克大戰(zhàn)繼續(xù)
碰撞檢測(cè)
在游戲開發(fā)中,通常把顯示圖像的對(duì)象叫做精靈Spire精靈需要有兩個(gè)屬性 image要顯示的圖像,rect圖像要顯示在屏幕的位置咖刃。
在Pygame框架中使用pygame.sprite模塊中的內(nèi)置函數(shù)可以實(shí)現(xiàn)碰撞檢測(cè)化漆。
pygame.sprite.collide_rect(first, second) #返回布爾值
pygame.sprite.Sprite是pygame精靈的基類,一般來說殊轴,總是需要寫一個(gè)自己的精靈類繼承pygame.sprite.Sprite衰倦。讓坦克類、子彈類都繼承編寫的精靈類梳凛。
音效處理
music是pygame中控制流音頻的pygame模塊耿币,音樂模塊與pygame.mixer緊密相連, pygame.mixer是一個(gè)用來處理聲音的模塊韧拒,其含義為“混音器”淹接。游戲中對(duì)聲音的處理一般包括制造聲音和播放聲音兩部分。使用pygame.mixer.music.load()加載一個(gè)播放音樂的文件pygame.mixer.music.play()開始播放音樂流叛溢。
小結(jié)
總算完成了塑悼,留有一點(diǎn)遺憾,就是敵方坦克在初始化的時(shí)候可能重疊坐標(biāo)楷掉,這樣會(huì)導(dǎo)致其無法移動(dòng)(穿墻限制)厢蒜,應(yīng)該很好處理,只是練習(xí)就愉快的跳過去了,我讓敵方坦克可以穿透任意坦克斑鸦,就行了愕贡,感覺這樣挺有意思:)。需要看源代碼的請(qǐng)?jiān)谏弦惶斓哪夸浝锩嬲业刂废镉欤揖筒毁N出來了固以。
花絮
想發(fā)布自己的小程序怎么辦?前面講過如何打包嘱巾,但是像游戲這樣的有資源文件憨琳,Pyinstaller如何將資源文件一起打包到一個(gè)app中呢?
基本原理:Pyinstaller 可以將資源文件一起bundle到exe中旬昭,當(dāng)exe在運(yùn)行時(shí)篙螟,會(huì)生成一個(gè)臨時(shí)文件夾,程序可通過sys._MEIPASS訪問臨時(shí)文件夾中的資源
官方說明:文檔
測(cè)試案例功能描述问拘,訪問資源文件夾res/a.txt遍略,并打印其內(nèi)容。實(shí)現(xiàn)方法如下:
源碼如下场梆,比較簡(jiǎn)單墅冷,resource_path方法說明了如何使用sys._MEIPASS變量來訪問臨時(shí)文件夾中的資源
#coding:utf-8
import sys
import os
#生成資源文件目錄訪問路徑
def resource_path(relative_path):
if getattr(sys, 'frozen', False): #是否Bundle Resource
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
#訪問res文件夾下a.txt的內(nèi)容
filename = resource_path(os.path.join("res","a.txt"))
print(filename)
with open(filename) as f:
lines = f.readlines()
print(lines)
f.close()
結(jié)下來介紹如何生成可執(zhí)行文件:
首先需要生成spec文件,pyi-makespec -F test.py (如果要添加Icon等可以在這里就使用pyi-makespec --icon abc.jpg -F test.py語句生成spec文件)或油。編輯spec文件寞忿,在datas選項(xiàng)中說明需要將哪些文件加入exe,在零時(shí)文件夾中命名成什么即可。
然后:pyinstaller -F test.spec
數(shù)據(jù)庫編程
數(shù)據(jù)庫的重要性不言而喻顶岸!
Python支持多種數(shù)據(jù)庫腔彰,從Python3.x版本開始,在標(biāo)準(zhǔn)庫中已經(jīng)內(nèi)置了SQLlite3模塊辖佣,它可以支持SQLite3數(shù)據(jù)庫的訪問和相關(guān)的數(shù)據(jù)庫操作霹抛。在需要操作SQLite3數(shù)據(jù)庫數(shù)據(jù)時(shí),只須在程序中導(dǎo)入SQLite3模塊即可卷谈。雖然SQLlite3用的比較廣泛杯拐,但實(shí)際遇到更多的還是Mysql,MongoDB,Redis等
SQLite3
Python語言操作SQLite3數(shù)據(jù)庫的基本流程如下所示世蔗。
- 導(dǎo)入相關(guān)庫或模塊(SQLite3)端逼。
- 使用connect()連接數(shù)據(jù)庫并獲取數(shù)據(jù)庫連接對(duì)象。它提供了以下方法:
- .cursor() 方法來創(chuàng)建一個(gè)游標(biāo)對(duì)象
- .commit() 方法來處理事務(wù)提交
- .rollback() 方法來處理事務(wù)回滾
- .close() 方法來關(guān)閉一個(gè)數(shù)據(jù)庫連接
- 使用con.cursor()獲取游標(biāo)對(duì)象污淋。
- 使用游標(biāo)對(duì)象的方法(execute()顶滩、executemany()、fetchall()等)來操作數(shù)據(jù)庫寸爆,實(shí)現(xiàn)插入礁鲁、修改和刪除操作盐欺,并查詢獲取顯示相關(guān)的記錄。
在Python程序中仅醇,連接函數(shù)sqlite3.connect()有如下兩個(gè)常用參數(shù)冗美。
- database:表示要訪問的數(shù)據(jù)庫名。
- timeout:表示訪問數(shù)據(jù)的超時(shí)設(shè)定着憨。
- 使用close()關(guān)閉游標(biāo)對(duì)象和數(shù)據(jù)庫連接墩衙。數(shù)據(jù)庫操作完成之后,必須及時(shí)調(diào)用其close()方法關(guān)閉數(shù)據(jù)庫連接甲抖,這樣做的目的是減輕數(shù)據(jù)庫服務(wù)器的壓力。
一個(gè)樣例代碼: 示例
Mysql
在Python3中大家用的比較多的還是pymysql模塊
安裝
pip3 install pymysql
基本使用
# coding:utf-8
import pymysql
#關(guān)于中文問題
#1. mysql命令行創(chuàng)建數(shù)據(jù)庫心铃,設(shè)置編碼為gbk:create databse demo2 character set utf8;
#2. python代碼中連接時(shí)設(shè)置charset="gbk"
#3. 創(chuàng)建表格時(shí)設(shè)置default charset=utf8
#連接數(shù)據(jù)庫
#charset和mysql服務(wù)端設(shè)置格式一樣(還可設(shè)置為gbk, gb2312)
conn = pymysql.connect(host="localhost", user="root", passwd="", db='test', charset='utf8', port=3306)
#創(chuàng)建游標(biāo)
cursor = conn.cursor()
try:
#執(zhí)行sql語句
cursor.execute("""create table if not exists t_sales(
id int primary key auto_increment not null,
nickName varchar(128) not null,
color varchar(128) not null,
size varchar(128) not null,
comment text not null,
saledate varchar(128) not null)engine=InnoDB default charset=utf8;""")
# cursor.execute("""insert into t_sales(nickName,color,size,comment,saledate)
# values('%s','%s','%s','%s','%s');""" % ("zack", "黑色", "L", "大小合適", "2019-04-20"))
cursor.execute("""insert into t_sales(nickName,color,size,comment,saledate)
values(%s,%s,%s,%s,%s);""" , ("zack", "黑色", "L", "大小合適", "2019-04-20"))
#提交
conn.commit()
### 插入數(shù)據(jù)
insert_sql = "insert into t_sales(nickName,color,size,comment,saledate) values(%s,%s,%s,%s,%s);"
#返回受影響的行數(shù)
row1 = cursor.execute(insert_sql,("Bob", "黑色", "XL", "便宜實(shí)惠", "2019-04-20"))
update_sql = "update t_sales set color='白色' where id=%s;"
#返回受影響的行數(shù)
row2 = cursor.execute(update_sql,(1,))
select_sql = "select * from t_sales where id>%s;"
#返回受影響的行數(shù)
row3 = cursor.execute(select_sql,(1,))
delete_sql = "delete from t_sales where id=%s;"
#返回受影響的行數(shù)
row4 = cursor.execute(delete_sql,(4,))
#提交准谚,不然無法保存新建或者修改的數(shù)據(jù)(增刪改得提交)
conn.commit()
### 批量插入
insert_sql = "insert into t_sales(nickName,color,size,comment,saledate) values(%s,%s,%s,%s,%s);"
data = [("Bob", "黑色", "XL", "便宜實(shí)惠", "2019-04-20"),("Ted", "黃色", "M", "便宜實(shí)惠", "2019-04-20"),("Gary", "黑色", "M", "穿著舒服", "2019-04-20")]
row1 = cursor.executemany(insert_sql, data)
conn.commit()
## 查詢數(shù)據(jù)
select_sql = "select id,nickname,size from t_sales where id>%s;"
cursor.execute(select_sql, (3,))
row1 = cursor.fetchone() #獲取第一條數(shù)據(jù),獲取后游標(biāo)會(huì)向下移動(dòng)一行
row_n = cursor.fetchmany(3) #獲取前n條數(shù)據(jù)去扣,獲取后游標(biāo)會(huì)向下移動(dòng)n行
row_all = cursor.fetchall() #獲取所有數(shù)據(jù)柱衔,獲取后游標(biāo)會(huì)向下移動(dòng)到末尾
print(row1)
print(row_n)
print(row_all)
# fetch獲取的數(shù)據(jù)默認(rèn)為元組格式,還可以獲取字典類型的愉棱,需要修改cursor如下如下:
# cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
#conn.commit()
except BaseException as e:
print(e)
finally:
#關(guān)閉游標(biāo)
cursor.close()
#關(guān)閉連接
conn.close()
注意execute執(zhí)行sql語句參數(shù)的兩種情況:
execute("insert into t_sales(nickName, size) values('%s','%s');" % ("zack","L") )
#此時(shí)的%s為字符竄拼接占位符唆铐,需要引號(hào)加'%s' (有sql注入風(fēng)險(xiǎn))
execute("insert into t_sales(nickName, size) values(%s,%s);" , ("zack","L"))
#此時(shí)的%s為sql語句占位符,不需要引號(hào)%s
多線程操控pymysql - 加鎖
對(duì)于pymysql模塊奔滑,通過多線程操控?cái)?shù)據(jù)庫容易出錯(cuò)艾岂,得加鎖串行執(zhí)行。進(jìn)行并發(fā)時(shí)朋其,可以利用DBUtils模塊來維護(hù)數(shù)據(jù)庫連接池王浴。
import pymysql
import threadind
#**************************無連接池*******************************
# 每個(gè)線程都要?jiǎng)?chuàng)立一次連接,線程并發(fā)操作間可能有問題梅猿?
def func():
conn = pymysql.connect(host="127.0.0.1",port=3306,db="test",user="root",passwd="",charset="utf8")
cursor = conn.cursor()
cursor.execute("select * from user where nid>1;")
result = cursor.fetchone()
print(result)
cursor.close()
conn.close()
if __name__=="__main__":
for i in range(5):
t = threading.Thread(target=func,name="thread-%s"%i)
t.start()
#**************************無連接池*******************************
#創(chuàng)建一個(gè)連接氓辣,加鎖串行執(zhí)行
from threading import Lock
import pymysql
import threading
conn = pymysql.connect(host="127.0.0.1",port=3306,db="test",user="root",passwd="",charset="utf8")
lock = Lock()
def func():
with lock:
cursor = conn.cursor()
cursor.execute("select * from user where nid>1;")
result = cursor.fetchone()
print(result)
cursor.close()
#conn.close()不能在線程中關(guān)閉連接,否則其他線程不可用了
if __name__=="__main__":
threads = []
for i in range(5):
t = threading.Thread(target=func,name="thread-%s"%i)
threads.append(t)
t.start()
for t in threads:
t.join()
conn.close()
多線程操作 - DBUtils連接池
DBUtils連接池有兩種連接模式:PersistentDB和PooledDB (官網(wǎng)文檔:https://cito.github.io/DBUtils/UsersGuide.html)
模式一(DBUtils.PersistentDB):為每個(gè)線程創(chuàng)建一個(gè)連接袱蚓,線程即使調(diào)用了close方法钞啸,也不會(huì)關(guān)閉,只是把連接重新放到連接池喇潘,供自己線程再次使用体斩。當(dāng)線程終止時(shí),連接自動(dòng)關(guān)閉响蓉。
PersistentDB使用代碼如下:
#coding:utf-8
from DBUtils.PersistentDB import PersistentDB
import pymysql
import threading
pool = PersistentDB(
creator = pymysql, # 使用鏈接數(shù)據(jù)庫的模塊
maxusage = None, # 一個(gè)鏈接最多被重復(fù)使用的次數(shù)硕勿,None表示無限制
setsession=[], # 開始會(huì)話前執(zhí)行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping = 0, # ping MySQL服務(wù)端枫甲,檢查是否服務(wù)可用源武。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
closeable = False, # 如果為False時(shí)扼褪, conn.close() 實(shí)際上被忽略,供下次使用粱栖,再線程關(guān)閉時(shí)话浇,才會(huì)自動(dòng)關(guān)閉鏈接。如果為True時(shí)闹究, conn.close()則關(guān)閉鏈接幔崖,那么再次調(diào)用pool.connection時(shí)就會(huì)報(bào)錯(cuò),因?yàn)橐呀?jīng)真的關(guān)閉了連接(pool.steady_connection()可以獲取一個(gè)新的鏈接)
threadlocal = None, # 本線程獨(dú)享值得對(duì)象渣淤,用于保存鏈接對(duì)象赏寇,如果鏈接對(duì)象被重置
host="127.0.0.1",
port = 3306,
user = "root",
password="",
database="test",
charset = "utf8"
)
def func():
conn = pool.connection()
cursor = conn.cursor()
cursor.execute("select * from user where nid>1;")
result = cursor.fetchone()
print(result)
cursor.close()
conn.close()
if __name__ == "__main__":
for i in range(5):
t = threading.Thread(target=func,name="thread-%s"%i)
t.start()
模式二(DBUtils.PooledDB):創(chuàng)建一批連接到連接池,供所有線程共享使用价认。
(由于pymysql嗅定、MySQLdb等threadsafety值為1,所以該模式連接池中的線程會(huì)被所有線程共享用踩。)
PooledDB使用代碼如下:
from DBUtils.PooledDB import PooledDB
import pymysql
import threading
import time
pool = PooledDB(
creator = pymysql, # 使用鏈接數(shù)據(jù)庫的模塊
maxconnections = 6, # 連接池允許的最大連接數(shù)渠退,0和None表示不限制連接數(shù)
mincached = 2, # 初始化時(shí),鏈接池中至少創(chuàng)建的空閑的鏈接脐彩,0表示不創(chuàng)建
maxcached = 5, # 鏈接池中最多閑置的鏈接碎乃,0和None不限制
maxshared = 3, # 鏈接池中最多共享的鏈接數(shù)量,0和None表示全部共享惠奸。PS: 無用梅誓,因?yàn)閜ymysql和MySQLdb等模塊的 threadsafety都為1,所有值無論設(shè)置為多少晨川,_maxcached永遠(yuǎn)為0证九,所以永遠(yuǎn)是所有鏈接都共享。
blocking = True, # 連接池中如果沒有可用連接后共虑,是否阻塞等待愧怜。True,等待妈拌;False拥坛,不等待然后報(bào)錯(cuò)
maxusage = None, # 一個(gè)鏈接最多被重復(fù)使用的次數(shù),None表示無限制
setsession = [], # 開始會(huì)話前執(zhí)行的命令列表尘分。如:["set datestyle to ...", "set time zone ..."]
ping = 0, # ping MySQL服務(wù)端猜惋,檢查是否服務(wù)可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host="127.0.0.1",
port = 3306,
user="root",
password="",
database = "test",
charset = "utf8"
)
def func():
conn = pool.connection()
cursor = conn.cursor()
cursor.execute("select * from user where nid>1;")
result = cursor.fetchone()
print(result)
time.sleep(5) #為了查看mysql端的線程數(shù)量
cursor.close()
conn.close()
if __name__=="__main__":
for i in range(5):
t = threading.Thread(target=func,name="thread-%s"%i)
t.start()
MongoDB
暫時(shí)不學(xué)
Redis
暫時(shí)不學(xué)
ORM
暫時(shí)不學(xué)
- sqlalchemy
- async-sqlalchemy