学习Python;如何让它更符合Python风格?
我是一名PHP开发者,正在探索外面的世界。我决定开始学习Python。
下面的脚本是我第一次尝试把一个PHP脚本转换成Python。这个脚本的功能是从Redis存储中获取推文。这些推文来自Twitter的流式API,并以JSON对象的形式存储。然后,它会提取所需的信息,并将其导出到一个CSV文件中,以便通过LOAD DATA LOCAL INFILE
导入到一个在不同服务器上的MySQL数据库中。
所以,我的问题是:现在我已经让我的第一个Python脚本运行了,我该如何让它更符合Python的风格呢?你们有什么建议吗?怎么让它更好?有没有我应该知道的技巧?欢迎建设性的批评!
更新:根据大家的建议,我更新了版本:
更新2:我用pylint检查了代码,现在得分是9.89/10。还有其他建议吗?
# -*- coding: utf-8 -*-
"""Redis IO Loop for Tweelay Bot"""
from __future__ import with_statement
import simplejson
import re
import datetime
import time
import csv
import hashlib
# Bot Modules
import tweelay.red as red
import tweelay.upload as upload
import tweelay.openanything as openanything
__version__ = "4"
def process_tweets():
"""Processes 0-20 tweets from Redis store"""
data = []
last_id = 0
for i in range(20):
last = red.pop_tweet()
if not last:
break
t = TweetHandler(last)
t.cleanup()
t.extract()
if t.get_tweet_id() == last_id:
break
tweet = t.proc()
if tweet:
data = data + [tweet]
last_id = t.get_tweet_id()
time.sleep(0.01)
if not data:
return False
ch = CSVHandler(data)
ch.pack_csv()
ch.uploadr()
source = "http://bot.tweelay.net/tweets.php"
openanything.openAnything(
source,
etag=None,
lastmodified=None,
agent="Tweelay/%s (Redis)" % __version__
)
class TweetHandler:
"""Cleans, Builds and returns needed data from Tweet"""
def __init__(self, json):
self.json = json
self.tweet = None
self.tweet_id = 0
self.j = None
def cleanup(self):
"""Takes JSON encoded tweet and cleans it up for processing"""
self.tweet = unicode(self.json, "utf-8")
self.tweet = re.sub('^s:[0-9]+:["]+', '', self.tweet)
self.tweet = re.sub('\n["]+;$', '', self.tweet)
def extract(self):
"""Takes cleaned up JSON encoded tweet and extracts the datas we need"""
self.j = simplejson.loads(self.tweet)
def proc(self):
"""Builds the datas from the JSON object"""
try:
return self.build()
except KeyError:
if 'delete' in self.j:
return None
else:
print ";".join(["%s=%s" % (k, v) for k, v in self.j.items()])
return None
def build(self):
"""Builds tuple from JSON tweet"""
return (
self.j['user']['id'],
self.j['user']['screen_name'].encode('utf-8'),
self.j['text'].encode('utf-8'),
self.j['id'],
self.j['in_reply_to_status_id'],
self.j['in_reply_to_user_id'],
self.j['created_at'],
__version__ )
def get_tweet_id(self):
"""Return Tweet ID"""
if 'id' in self.j:
return self.j['id']
if 'delete' in self.j:
return self.j['delete']['status']['id']
class CSVHandler:
"""Takes list of tweets and saves them to a CSV
file to be inserted into MySQL data store"""
def __init__(self, data):
self.data = data
self.file_name = self.gen_file_name()
def gen_file_name(self):
"""Generate unique file name"""
now = datetime.datetime.now()
hashr = hashlib.sha1()
hashr.update(str(now))
hashr.update(str(len(self.data)))
hash_str = hashr.hexdigest()
return hash_str+'.csv'
def pack_csv(self):
"""Save tweet data to CSV file"""
with open('tmp/'+self.file_name, mode='ab') as ofile:
writer = csv.writer(
ofile, delimiter=',',
quotechar='"',
quoting=csv.QUOTE_MINIMAL)
writer.writerows(self.data)
def uploadr(self):
"""Upload file to remote host"""
url = "http://example.com/up.php?filename="+self.file_name
uploadr = upload.upload_file(url, 'tmp/'+self.file_name)
if uploadr[0] == 200:
print "Upload: 200 - ("+str(len(self.data))+")", self.file_name
print "-------"
#os.remove('tmp/'+self.file_name)
else:
print "Upload Error:", uploadr[0]
if __name__ == "__main__":
while True:
process_tweets()
time.sleep(1)
7 个回答
如果你有一个方法的代码太长,放不下在视图窗口里,那你真的需要把它缩短一下。比如说,控制在15行左右。我看到的代码里至少有三个方法:print_tweet、save_csv和upload_data。虽然不太好说这些方法应该叫什么名字,但看起来确实有三个不同的代码部分,你应该尝试把它们分开。
在Python中,通常不太使用整数来控制流程。我们几乎总是用这种写法:for item in container:
。另外,我建议用一个类来保存“用户对象”。这样比用简单的容器类型,比如列表和字典,要容易得多(而且能让你的代码更符合面向对象的风格)。你还可以提前编译正则表达式,这样能稍微提高一些性能。
class MyTweet(object):
def __init__(self, data):
# ...process json here
# ...
self.user = user
for data in getTweets():
tweet = MyTweet(data)
可以用更简洁的方式来写代码:
i=0
end=20
last_id=0
data=[]
while(i<=end):
i = i + 1
...
代码:
last_id=0
data=[]
for i in xrange(1, 22):
...
这样写意思是一样的,但更紧凑,也更符合Python的风格。
比如说,
if not last or last == None:
可以直接写
if not last:
因为 None
本身就被认为是“假”的(所以当 last
是 None
时, not last
就是 True
)。一般来说,如果你想检查某个东西是否是 None
,用 code is None
就可以了,而不是 code == None
。
在
if(j['id'] <> last_id):
可以去掉多余的括号和过时的 <>
操作符,直接用
if j['id'] != last_id:
同时也可以把其他 if
语句中的多余括号去掉。
比如说:
if len(data) == 0:
代码:
if not data:
因为任何空的容器都被认为是“假”的。
在
hash_str = str(hash.hexdigest())
可以用
hash_str = hash.hexdigest()
因为这个方法已经返回了一个字符串,所以调用 str
是多余的。
再比如:
for item in data:
writer.writerow(item)
可以用
writer.writerows(data)
这样可以让循环自动处理。
而不是
ofile = open('tmp/'+file_name, mode='ab')
...
ofile.close()
在Python 2.6或更高版本中使用,或者在2.5中通过
from __future__ import with_statement
来“引入未来”的 with
语句功能:
with open('tmp/'+file_name, mode='ab') as ofile:
...
这样可以确保在你使用完后自动关闭(即使在出现异常的情况下也能处理)。
而不是
print "Upload Error: "+uploadr[0]
可以用
print "Upload Error:", uploadr[0]
对于其他 print
语句也是一样——逗号会自动为你插入一个空格。
我相信还有更多这样的细节,但这些是在我浏览你的代码时“一下子就看到了”的一些小问题。