学习Python;我怎样才能让这更像Pythonic?

时间:2010-08-01 22:36:48

标签: python

我是一名探索外部世界的PHP开发人员。我决定开始学习Python。

以下脚本是我第一次尝试将PHP脚本移植到Python。它的工作是从Redis商店获取推文。这些推文来自Twitter的Streaming API并存储为JSON对象。然后,提取所需信息并将其转储到CSV文件中,以使用托管在不同服务器上的LOAD DATA LOCAL INFILE导入MySQL。

所以,问题是:现在我的第一个Python脚本正在运行,我怎样才能让它更像Pythonic?你们有什么建议吗?让它变得更好?我应该知道的诀窍?有建设性的批评?

更新:根据目前为止的每个人的建议,这里是更新版本:
Update2:通过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 个答案:

答案 0 :(得分:19)

而不是:

  i=0
  end=20
  last_id=0
  data=[]
  while(i<=end):
    i = i + 1
    ...

代码:

  last_id=0
  data=[]
  for i in xrange(1, 22):
    ...

相同的语义,更紧凑和Pythonic。

而不是

if not last or last == None:

只做

if not last:

因为None无论如何都是假的{not last TruelastNone). In general, when you want to check if something is时无, code为无{{1} }} ==无`。

, not

丢失冗余括号和过时的 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)

使用(在Python 2.6或更高版本中,或在2.5中使用

启动模块)
  ofile = open('tmp/'+file_name, mode='ab')
  ...
  ofile.close()    

“从未来导入” 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] 语句类似 - 逗号为您插入空格。

我确信还有更多这样的小东西,但是当我扫描你的代码时,这些东西“跳到了眼前”。

答案 1 :(得分:6)

Pythonic python不会非常使用整数流控制。这个成语几乎总是for item in container:。另外,我会使用一个类来保存“用户对象”。它比简单的容器类型(如列表和词典)更容易使用(并且将代码安排到更多的OO样式。)您可以预先编译注册表以获得更高的性能。

class MyTweet(object):
  def __init__(self, data):
    # ...process json here
    # ...
    self.user = user

for data in getTweets():
  tweet = MyTweet(data)

答案 2 :(得分:2)

# Bot Modules
import red #Simple Redis API functions
import upload #pycurl script to upload to remote server

如果您的应用程序将被使用和维护,最好将所有这些模块打包在包中。

答案 3 :(得分:2)

而不是......

  i=0
  end=20
  last_id=0
  data=[]
  while(i<=end):
    i = i + 1

你可以使用......

for i in range(20):

但总的来说,这20来自何处并不十分清楚?魔术#?

答案 4 :(得分:2)

如果您的方法不适合视图窗格,那么您确实希望缩短它。说15行左右。我看到至少有3种方法:print_tweet,save_csv和upload_data。要确切地说出它们应该被命名的内容有点难以确定,但似乎有三个不同的代码段应该尝试突破。

答案 5 :(得分:2)

通过pylint运行您的代码。

答案 6 :(得分:1)

  1. 我在Python中见过的每个方法变量名都是小写的,没有下划线。 (我不认为这是一项要求,可能不是标准做法。)
  2. 你应该把逻辑分解成多个单一用途的方法。
  3. 更进一步,创建一些类来将相关方法封装在一起。