如何用Python编写这个代码片段?
我正在学习Python(之前有C/C++的背景)。
不过,我想在学习的同时写一些实际的东西。我有以下的伪代码(这是我第一次尝试写Python脚本,自从昨天开始了解Python)。希望这个代码片段能详细说明我想做的逻辑。顺便说一下,我在Ubuntu Karmic上使用的是Python 2.6。
假设这个脚本是这样调用的:script_name.py directory_path
import csv, sys, os, glob
# Can I declare that the function accepts a dictionary as first arg?
def getItemValue(item, key, defval)
return !item.haskey(key) ? defval : item[key]
dirname = sys.argv[1]
# declare some default values here
weight, is_male, default_city_id = 100, true, 1
# fetch some data from a database table into a nested dictionary, indexed by a string
curr_dict = load_dict_from_db('foo')
#iterate through all the files matching *.csv in the specified folder
for infile in glob.glob( os.path.join(dirname, '*.csv') ):
#get the file name (without the '.csv' extension)
code = infile[0:-4]
# open file, and iterate through the rows of the current file (a CSV file)
f = open(infile, 'rt')
try:
reader = csv.reader(f)
for row in reader:
#lookup the id for the code in the dictionary
id = curr_dict[code]['id']
name = row['name']
address1 = row['address1']
address2 = row['address2']
city_id = getItemValue(row, 'city_id', default_city_id)
# insert row to database table
finally:
f.close()
我有以下几个问题:
这段代码写得够Python风格吗(有没有更好的实现方式)?
给定一个如下所示的表格结构,我该如何写一个Python函数,从表中获取数据,并以字符串(名字)为索引返回一个字典?
我该如何将行数据插入到表中(实际上我想使用事务,如果可能的话,在文件关闭之前提交)?
表格结构:
create table demo (id int, name varchar(32), weight float, city_id int);
顺便提一下,我的后端数据库是PostgreSQL。
[编辑]
Wayne等人:
为了澄清,我想要的是一组行。每一行可以通过一个键来索引(这意味着行的容器是一个字典,对吧?)。好吧,现在一旦我们通过键获取了一行,我还想能够访问这一行中的“列”——这意味着行数据本身也是一个字典。我不知道Python在处理字典时是否支持多维数组语法,但以下语句可以帮助解释我打算如何概念性地使用从数据库返回的数据。像dataset['joe']['weight']这样的语句会首先获取由键'joe'索引的行数据(这是一个字典),然后再从这个字典中索引'weight'。我想知道如何以Python风格构建这样的字典的字典,从获取的数据中。
一种简单的方法是写类似这样的代码:
import pyodbc
mydict = {}
cnxn = pyodbc.connect(params)
cursor = cnxn.cursor()
cursor.execute("select user_id, user_name from users"):
for row in cursor:
mydict[row.id] = row
这样写对吗?或者有没有更Python风格的写法?
3 个回答
在def
后面需要冒号:
def getItemValue(item, key, defval):
...
布尔运算符:在Python中,!
变成not
;&&
变成and
,||
变成or
(关于布尔运算符的更多信息,可以查看这个链接)。Python里没有? :
这个运算符,不过有一个return (x) if (x) else (x)
的表达式,虽然我个人很少用这个,通常还是用简单的if
。
布尔值和None
:True
、False
和None
的首字母都是大写的。
检查参数类型:在Python中,通常不需要声明函数参数的类型。你可以在函数里用例如assert isinstance(item, dict), "字典必须作为第一个参数传入!"
来检查,虽然这种“严格检查”在Python中并不总是必要,所以通常不推荐。
Python关键字:default
并不是一个保留的Python关键字,可以用作参数和变量(仅供参考)。
风格指南:PEP 8(Python的风格指南)建议每行一般只写一个模块import
,虽然有些例外(我得承认,我通常不把import sys
和os
放在不同的行上,不过其他地方我通常会遵循这个规则)。
文件打开模式:rt
在Python 2.x中是无效的——虽然它会运行,但t
会被忽略。更多信息可以查看这个链接。在Python 3中是有效的,所以如果你想强制使用文本模式,抛出二进制字符的异常也没问题(如果你想读取非ASCII字符,可以用rb
)。
处理字典:Python以前使用dict.has_key(key)
,现在应该用key in dict
(这个方法已经基本取代了前者,更多信息可以查看这个链接)。
分割文件扩展名:code = infile[0:-4]
可以用code = os.path.splitext(infile)[0]
替代(这个方法返回例如('root', '.ext')
,扩展名前有一个点,更多信息可以查看这个链接)。
编辑:去掉了在一行中声明多个变量的内容,并添加了一些格式调整。还纠正了rt
在Python 3中是有效模式的错误。
看起来这段代码对我来说基本上是符合Python风格的。
不过,三元运算符应该像这样写(我觉得这样能返回你想要的结果):
return defval if not key in item else item[key]
是的,你可以以基本上任何顺序传递一个字典(或者其他任何值)。唯一的区别是如果你使用 *args 和 **kwargs(这个名字是约定俗成的,实际上你可以用任何你想要的名字),它们需要按特定的顺序传递,并且最后一个或两个参数。
如果要插入数据到数据库,你可以使用odbc模块:
import odbc
conn = odbc.odbc('servernamehere')
cursor = conn.cursor()
cursor.execute("INSERT INTO mytable VALUES (42, 'Spam on Eggs', 'Spam on Wheat')")
conn.commit()
你可以查阅或找到很多关于odbc模块的例子——我相信还有其他模块,但这个应该对你来说没问题。
要获取数据,你可以使用
cursor.execute("SELECT * FROM demo")
#Reads one record - returns a tuple
print cursor.fetchone()
#Reads the rest of the records - a list of tuples
print cursor.fetchall()
把其中一条记录变成一个字典:
record = cursor.fetchone()
# Removes the 2nd element (at index 1) from the record
mydict[record[1]] = record[:1] + record[2:]
不过,如果你想一次性获取所有数据,这几乎是需要用生成器表达式的。
mydict = dict((record[1], record[:1] + record[2:] for record in cursor.fetchall())
这样可以把所有记录整齐地打包成一个字典,使用名字作为键。
希望这对你有帮助。
要从字典中获取值,你需要使用.get
这个方法:
>>> d = {1: 2}
>>> d.get(1, 3)
2
>>> d.get(5, 3)
3
这样就不需要getItemValue
这个函数了。我不想评论现有的语法,因为它显然不符合Python的风格。Python中三元运算符的正确语法是:
true_val if true_false_check else false_val
>>> 'a' if False else 'b'
'b'
不过正如我下面所说的,你根本不需要它。
如果你使用的是Python版本大于2.6的,建议使用with
语句,而不是try-finally
:
with open(infile) as f:
reader = csv.reader(f)
... etc
看到你想把row
当作字典使用,你应该使用csv.DictReader
,而不是简单的csv.reader
。不过在你的情况下,这并不是必要的。你的SQL查询可以直接构造来访问row
字典的字段。在这种情况下,你就不需要单独创建city_id
、name
等项。如果想在row
中添加默认的city_id
(如果它不存在的话),可以使用.setdefault
方法:
>>> d
{1: 2}
>>> d.setdefault(1, 3)
2
>>> d
{1: 2}
>>> d.setdefault(3, 3)
3
>>> d
{1: 2, 3: 3}
对于id
,你可以简单地写row[id] = curr_dict[code]['id']
在切片时,你可以跳过0
:
>>> 'abc.txt'[:-4]
'abc'
通常,Python的库在游标上提供了fetchone
、fetchmany
、fetchall
这些方法,它们返回Row
对象,这个对象可能支持像字典一样的访问,或者返回一个简单的元组。这取决于你使用的具体模块。