加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

Python中的一些陷阱与技巧小结

发布时间:2020-05-23 16:34:14 所属栏目:Python 来源:互联网
导读:Python是一种被广泛使用的强大语言,让我们深入这种语言,并且学习一些控制语句的技巧,标准库的窍门和一些常见的陷阱。

Python是一种被广泛使用的强大语言,让我们深入这种语言,并且学习一些控制语句的技巧,标准库的窍门和一些常见的陷阱。

Python(和它的各种库)非常庞大。它被用于系统自动化、web应用、大数据、数据分析及安全软件。这篇文件旨在展示一些知之甚少的技巧,这些技巧将带领你走上一条开发速度更快、调试更容易并且充满趣味的道路。

学习Python和学习所有其他语言一样,真正有用的资源不是各个语言繁琐的超大官方文档,而是使用常用语法、库和Python社区共享知识的能力。
探索标准数据类型
谦逊的enumerate

遍历在Python中非常简单,使用“for foo in bar:”就可以。
 

drinks = ["coffee","tea","milk","water"]
for drink in drinks:
  print("thirsty for",drink)
#thirsty for coffee
#thirsty for tea
#thirsty for milk
#thirsty for water

但是同时使用元素的序号和元素本身也是常见的需求。我们经常看到一些程序员使用len()和range()来通过下标迭代列表,但是有一种更简单的方式。
 

drinks = ["coffee","water"]
for index,drink in enumerate(drinks):
  print("Item {} is {}".format(index,drink))
#Item 0 is coffee
#Item 1 is tea
#Item 2 is milk
#Item 3 is water

enumerate 函数可以同时遍历元素及其序号。
Set类型

许多概念都可以归结到对集合(set)的操作。例如:确认一个列表没有重复的元素;查看两个列表共同的元素等等。Python提供了set数据类型以使类似这样的操作更快捷更具可读性。
 

# deduplicate a list *fast*
print(set(["ham","eggs","bacon","ham"]))
# {'bacon','eggs','ham'}
 
# compare lists to find differences/similarities
# {} without "key":"value" pairs makes a set
menu = {"pancakes","ham","bacon"}
new_menu = {"coffee","bagels"}
 
new_items = new_menu.difference(menu)
print("Try our new",",".join(new_items))
# Try our new bagels,coffee
 
discontinued_items = menu.difference(new_menu)
print("Sorry,we no longer have",".join(discontinued_items))
# Sorry,we no longer have pancakes
 
old_items = new_menu.intersection(menu)
print("Or get the same old",".join(old_items))
# Or get the same old eggs,bacon,ham
 
full_menu = new_menu.union(menu)
print("At one time or another,we've served:",".join(full_menu))
# At one time or another,we've served: coffee,ham,pancakes,bagels,eggs

intersection 函数比较列表中所有元素,返回两个集合的交集。在我们的例子中,早餐的主食为bacon、eggs和ham。
collections.namedtuple

如果你不想给一个类添加方法,但又想使用foo.prop的调用方式,那么你需要的就是namedtuple。你提前定义好类属性,然后就可以实例化一个轻量级的类,这样的方式会比完整的对象占用更少的内存。
 

LightObject = namedtuple('LightObject',['shortname','otherprop'])
m = LightObject()
m.shortname = 'athing'
> Traceback (most recent call last):
> AttributeError: can't set attribute

用这种方式你无法设置namedtuple的属性,正如你不能修改元组(tuple)中元素的值。你需要在实例化namedtuple的时候设置属性的值。
 

LightObject = namedtuple('LightObject','otherprop'])
n = LightObject(shortname='something',otherprop='something else')
n.shortname 
# something
collections.defaultdict

在写Python应用使用字典时,很多时候有些关键字一开始并不存在,例如下面的例子。
 

login_times = {}
for t in logins:
  if login_times.get(t.username,None):
    login_times[t.username].append(t.datetime)
  else:
    login_times[t.username] = [t.datetime]

使用defaultdict 我们可以跳过检查关键字是否存在的逻辑,对某个未定义key的任意访问,都会返回一个空列表(或者其他数据类型)。
 

login_times = collections.defaultdict(list)
for t in logins:
  login_times[t.username].append(t.datetime)

你甚至可以使用自定义的类,这样调用的时候实例化一个类。
 

from datetime import datetime
class Event(object):
  def __init__(self,t=None):
  if t is None:
    self.time = datetime.now()
  else:
    self.time = t
 
events = collections.defaultdict(Event)
 
for e in user_events:
  print(events[e.name].time)

如果既想具有defaultdict的特性,同时还想用访问属性的方式来处理嵌套的key,那么可以了解一下 addict。
 

normal_dict = {
  'a': {
    'b': {
      'c': {
        'd': {
          'e': 'really really nested dict'
        }
      }
    }
  }
}
 
from addict import Dict
addicted = Dict()
addicted.a.b.c.d.e = 'really really nested'
print(addicted)
# {'a': {'b': {'c': {'d': {'e': 'really really nested'}}}}}

这段小程序比标准的dict要容易写的多。那么为什么不用defaultdict呢? 它看起来也够简单了。
 

from collections import defaultdict
default = defaultdict(dict)
default['a']['b']['c']['d']['e'] = 'really really nested dict' 
# fails

这段代码看起来没什么问题,但是它最终抛出了KeyError异常。这是因为default[‘a']是dict,不是defaultdict.让我们构造一个value是defaulted dictionaries类型的defaultdict,这样也只能解决两级嵌套。

如果你只是需要一个默认计数器,你可以使用collection.Counter,这个类提供了许多方便的函数,例如 most_common.
控制流

当学习Python中的控制结构时,通常要认真学习 for,while,if-elif-else,和 try-except。只要正确使用,这几个控制结构能够处理绝大多数的情况。也是基于这个原因,几乎你所遇到的所有语言都提供类似的控制结构语句。在基本的控制结构以外,Python也额外提供一些不常用的控制结构,这些结构会使你的代码更具可读性和可维护性。
Great Exceptations

Exceptions作为一种控制结构,在处理数据库、sockets、文件或者任何可能失败的资源时非常常用。使用标准的 try 、except 结构写数据库操作时通常是类型这样的方式。
 

try:
  
# get API data
  data = db.find(id='foo') 
# may raise exception
  
# manipulate the data
  db.add(data)
  
# save it again
  db.commit() 
# may raise exception
except Exception:
  
# log the failure
  db.rollback()
 
db.close()

你能发现这里的问题吗?这里有两种可能的异常会触发相同的except模块。这意味着查找数据失败(或者为查询数据建立连接失败)会引发回退操作。这绝对不是我们想要的,因为在这个时间点上事务并没有开始。同样回退也不应该是数据库连接失败的正确响应,因此让我们将不同的情况分开处理。

首先,我们将处理查询数据。

try:
  
# get API data
  data = db.find(id='foo') 
# may raise exception
except Exception:
  
# log the failure and bail out
  log.warn("Could not retrieve FOO")
  return
 
# manipulate the data
db.add(data)

现在数据检索拥有自己的try-except,这样当我们没有取得数据时,我们可以采取任何处理方式。没有数据我们的代码不大可能再做有用的事,因此我们将仅仅退出函数。除了退出你也可以构造一个默认对象,重新进行检索或者结束整个程序。

现在让我们将commit的代码也单独包起来,这样它也能更优雅的进行错误处理。
 

try:
  db.commit() 
# may raise exception
except Exception:
  log.warn("Failure committing transaction,rolling back")
  db.rollback()
else:
  log.info("Saved the new FOO")
finally:
  db.close()

实际上,我们已经增加了两端代码。首先,让我们看看else,当没有异常发生时会执行这里的代码。在我们的例子中,这里只是将事务成功的信息写入日志,但是你可以按照需要进行更多有趣的操作。一种可能的应用是启动后台任务或者通知。

很明显finally 子句在这里的作用是保证db.close() 总是能够运行。回顾一下,我们可以看到所有和数据存储相关的代码最终都在相同的缩进级别中形成了漂亮的逻辑分组。以后需要进行代码维护时,将很直观的看出这几行代码都是用于完成 commit操作的。
Context and Control

之前,我们已经看到使用异常来进行处理控制流。通常,基本步骤如下:

  •     尝试获取资源(文件、网络连接等)
  •     如果失败,清除留下的所有东西
  •     成功获得资源则进行相应操作
  •     写日志
  •     程序结束

考虑到这一点,让我们再看一下上一章数据库的例子。我们使用try-except-finally来保证任何我们开始的事务要么提交要么回退。
 

try:
  
# attempt to acquire a resource
  db.commit()
except Exception:
  
# If it fails,clean up anything left behind
  log.warn("Failure committing transaction,rolling back")
  db.rollback()
else:
  
# If it works,perform actions
  
# In this case,we just log success
  log.info("Saved the new FOO")
finally:
  
# Clean up
  db.close()
# Program complete

我们前面的例子几乎精确的映射到刚刚提到的步骤。这个逻辑变化的多吗?并不多。

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读