Python编程高级技巧 选择好的名称_library utils common core

在这种类型的程序库环境中,得体的做法是只对暴露到框架中的元素使用混合大小写,而其他代码保持遵守PEP 8风格。

还值得注意的是,Twisted项目的开发人员采用一种完全不同的方法来解决这个问题。与Zope一样,Twisted项目早于PEP 8文档。项目启动时没有任何代码风格的官方指南,所以它有自己的风格指南。关于缩进、文档字符串、每行长度等风格规则可以很容易被采用。另一方面,修改所有代码以匹配PEP 8的命名约定,可能会完全破坏向后兼容。对于像Twisted这样的大型项目而言,这么做并不可行。因此Twisted尽可能遵守PEP 8,并将其余内容(例如变量、函数和方面的混合大小写)作为它自己的编码标准的一部分。这与PEP 8的建议完全兼容,因为它特别强调,在项目内的一致性比遵守PEP 8风格指南要更加重要。

5.关于私有元素的争论

对于私有方法和函数,惯例是添加一个前缀下划线。考虑到Python中的名称修饰(name-mangling)特性,这条规则是相当有争议的。如果一个方法有两个前缀下划线,它会在运行时被解释器重命名,以避免与任何子类中的方法产生命名冲突。

因此,有些人倾向于对私有属性使用双前缀下划线,以避免子类中的命名冲突:

  class Base(object):
      def __secret(self):
          print("don't tell")
      def public(self):
          self.__secret()
  class Derived(Base):
      def __secret(self):
          print("never ever")

你将会看到以下内容:

>>> Base.__secret

Traceback (most recent call last):

 File "<input>", line 1, in <module>



AttributeError: type object 'Base' has no attribute '__secret'



>>> dir(Base)

['_Base__secret', ..., 'public']

>>> Derived().public()

don't tell

Python中名称修饰的最初目的不是提供类似C++的私有花招(gimmick),而是用来确保某些基类隐式地避免子类中的冲突,特别是在多重继承的上下文中。但将其用于每个属性则会使私有代码含义变得模糊,这一点也不Pythonic。

因此,有些人认为应该始终使用显式的名称修饰:

  class Base:
      def _Base_secret(self):  # 不要这么做!!!
          print("you told it ?")

这样会在所有代码中重复类名,所以应该首选__

但正如BDFL(Guido,the Benevolent Dictator For Life,参见http://en.wikipedia.org/wiki/ BDFL)所说,最佳做法是在编写子类中的方法之前查看该类的__mro__(方法解析顺序)值,从而避免使用名称修饰。修改基类的私有方法一定要小心。

关于这个主题的更多信息,许多年前在Python-Dev邮件列表中出现过一个有趣的讨论,人们争论名称修饰的实用性以及它在这门语言中的命运。你可以访问地网址:http://mail.python.org/ pipermail/python-dev/2005-December/058555.html查看。

6.特殊方法

特殊方法(https://docs.python.org/3/reference/datamodel.html#special-method-names)以双下划线开始和结束,常规的方法不应该使用这种约定。有些开发者曾经将其称为dunder方法,作为双下划线(double-underscore)的合成词。它们可用于运算符重载、容器定义等方面。为了保证可读性,它们应该集中放在类定义的开头:

  class WeirdInt(int):
      def __add__(self, other):
          return int.__add__(self, other) + 1
      def __repr__(self):
          return '<weirdo %d>' % self
      # 公共API
      def do_this(self):
          print('this')
      def do_that(self):
          print('that')

对于常规方法而言,你永远不应该使用这种名称。所以不要为方法创建这样的名称:

  class BadHabits:
      def __my_method__(self):
          print('ok')
7.参数

参数名称使用小写,如果需要的话可以加下划线。它们遵循与变量相同的命名规则。

8.property

property的名称使用小写或小写加下划线。大部分时候,它们表示一个对象的状态,可以是名词或形容词,如果需要的话也可以是如下简短的短语:

  class Connection:
      _connected = []
      def connect(self, user):
          self._connected.append(user)
      @property
      def connected_people(self):
          return ', '.join(self._connected)

在交互式会话中运行的结果如下所示:

>>> connection = Connection()

>>> connection.connect('Tarek')

>>> connection.connect('Shannon')

>>> print(connection.connected_people)


Tarek, Shannon
9.类

类名称始终采用驼峰式命名法,如果它们是模块的私有类,还可能有一个前缀下划线。

类和实例变量通常是名词短语,与用动词短语命名的方法名称构成使用逻辑:

  class Database:
      def open(self):
          pass
  class User:
      pass

下面是在交互式会话中的使用示例:

>>> user = User()

>>> db = Database()

>>> db.open()
10.模块和包

除了特殊模块__init__之外,模块名称都使用小写,不带下划线。

下面是标准库中的一些例子:

  • os
  • sys
  • shutil

如果模块是包的私有模块,则添加一个前缀下划线。编译过的C或C++模块名称通常带有一个下划线,并在纯Python模块中导入。

包名称遵循同样的规则,因为它的表现就像是更加结构化的模块。

3 命名指南

一组常用的命名规则可以被应用于变量、方法、函数和property。类和模块的名称也在命名空间的构建中具有重要的作用,从而也影响代码可读性。本迷你指南为挑选名称提供了常见的模式和反模式。

3.1 用“has”或“is”前缀命名布尔元素

如果一个元素保存的是布尔值,ishas前缀提供一种自然的方式,使其在命名空间中的可读性更强,代码如下:

  class DB:
      is_connected = False
      has_cache = False
3.2 用复数形式命名集合变量

如果一个元素保存的是集合变量,那么使用复数形式是一个好主意。有些映射在暴露为序列时也可以从中受益:

  class DB:
      connected_users = ['Tarek']
      tables = {
          'Customer': ['id', 'first_name', 'last_name']
      }
3.3 用显式名称命名字典

如果一个变量保存的是映射,那么你应该尽可能使用显式名称。例如,如果一个字典保存的是个人地址,那么可以将其命名为persons_addresses,代码如下:

  persons_addresses = {'Bill': '6565 Monty Road',
                       'Pamela': '45 Python street'}
  persons_addresses['Pamela']
  '45 Python street'
3.4 避免通用名称

如果你的代码不是在构建一种新的抽象数据类型,那么使用类似listdictsequenceelements等专用名词是有害的,即使对于局部变量也一样。它使得代码难以阅读、理解和使用。还应该避免使用内置名称,以避免在当前命名空间中将其屏蔽(shadowing)。还应该避免使用通用的动词,除非它们在该命名空间中有意义。

相反,应该使用领域特定的术语,如下所示:

  def compute(data):  # 太过通用
      for element in data:
          yield element ** 2
  def squares(numbers):  # 更好一些
      for number in numbers:
          yield number ** 2

还有一系列前缀和后缀,虽然在编程中非常常见,但事实上应该避免出现在函数和类名称中:

  • manager;
  • object;
  • do、handle或perform。

这样做的原因是它们的含义模糊、模棱两可,且没有向实际名称中添加任何信息。Jeff Atwood(Discourse 和Stack Overflow的联合创始人)关于这个话题写过一篇非常好的文章,你可以在他的博客上找到这篇文章:http://blog.codinghorror.com/i-shall-call-it-somethingmanager/。

还有许多包的名称应该避免。任何没有对其内容给出任何信息的名称,从长远来看都对项目有很大害处。诸如misctoolsutilscommoncore的名称有很大可能会变成一大堆不相关的、质量非常差的代码片段,其大小呈指数增长。在大多数情况下,这种模块的存在是懒惰或缺乏足够设计经验的迹象。热衷于这种模块名称的人可以预见未来,并将其重命名为trash(垃圾箱)或dumpster(垃圾桶),因为这正是他们的队友最终对这些模块的处理方式。

在大多数情况下,更多的小模块几乎总是更好,即使内容很少,但名称可以很好地反映其内容。说实话,类似utilscommon之类的名称没有本质错误,你可以负责任地使用它们。但现实表明,在许多情况下,它们都会变为危险的结构反模式,并且迅速增长。而且如果你的行动不够快,你可能永远无法摆脱它们。因此,最好的方法就是避免这样有风险的组织模式,如果项目其他人引入的话则将其扼杀在萌芽状态。

3.5 避免现有名称

使用上下文中已经存在的名称是不好的做法,因为它会导致阅读代码时——特别是调试时——非常混乱,例如以下代码:

>>> def bad_citizen():

...     os = 1

...     import pdb; pdb.set_trace()

...     return
os
...

>>> bad_citi
zen()

><stdin>(4)bad_cit
izen()

(Pdb) os1

(Pdb) 
import os

(Pdb) c

<module 'os' from '/Library/Frameworks/Python.framework/Versio
ns/2.5/lib/

python2.5/os.pyc'>

在这个例子中,os名称被代码屏蔽。内置函数名称和来自标准库的模块名称都应该避免。

尽量使用原创的名称,即使是上下文的局部名称。对于关键字而言,后缀下划线是一种避免冲突的方法:

  def xapian_query(terms, or_=True):
      """if or_ is true, terms are combined with the OR clause"""
      ...

注意,class通常被替换为klasscls

  def factory(klass, *args, **kwargs):
      return klass(*args, **kwargs)

4 参数的最佳实践

函数和方法的签名是代码完整性的保证,它们驱动函数和方法的使用并构建其API。除了我们之前看到的命名规则之外,对参数也要特别小心。这可以通过3个简单的规则来实现。

  • 通过迭代设计构建参数。
  • 信任参数和测试。
  • 小心使用魔法参数*args**kwargs
4.1 通过迭代设计构建参数

如果每个函数都有一个固定的、定义明确的参数列表,那么代码的鲁棒性会更好。但这在第一个版本中无法完成,所以参数必须通过迭代设计来构建。它们应该反映创建该元素所针对的使用场景,并相应地逐渐发展。

例如,如果添加了一些参数,它们应该尽可能有默认值,以避免任何退化:

  class Service:  # 版本1

      def _query(self, query, type)
:
          print('done
')

    def execute(self, que
ry):
          self._query(query, 'EXECUTE')

  
>>> Service().execute('my query')


done

  impo
rt logging

  class Service(obj
ect):  # 版本2
     def _query(self, query, t
ype, logger):
          
logger('done')

     def execute(self, query, logge
r=logging.info):
          self._query(query, '
EXECUTE', logger)

  
>>> Service().execute('m
y query')    # 旧式调用


>>> Service().execute('my quer
y', logging.warning)

WARNING:root:done

如果一个公共元素的参数必须被修改,那么将使用一个deprecation进程,本节稍后将对此进行说明。

4.2 信任参数和测试

考虑到Python的动态类型特性,有些开发人员在函数和方法的顶部使用断言(assertion)来确保参数具有正确的内容,代码如下:

  def division(dividend, divisor):

      assert isinstance(dividend, (int, float)
)
      assert isinstance(divisor, (int, float
))
      return dividend / divisor

>>> division(
2, 4)

0.5

>>> division(2, None)


Traceback (most recent cal
l last):

 File "<input>", line 1, in
<module>

 File "<input>", line 3, in division


AssertionError

这通常是那些习惯于静态类型、并且感觉Python中缺少点什么的开发者的做法。

这种检查参数的方法是契约式设计Design by Contract,DbC,参见http://en.wikipedia.org/ wiki/DesignByContract)编程风格的一部分。在这种设计中,在代码实际运行之间会检查先决条件。

这种方法有两个主要问题。

  • DbC的代码对应该如何使用它进行解释,导致其可读性降低。
  • 这可能使代码速度变慢,因为每次调用都要进行断言。

后者可以通过解释器的"-O"选项来避免。在这种情况下,在创建字节码之前,所有断言都将从代码中删除,这样检查也就会丢失。

在任何情况下,断言都必须小心进行,并且不应该用于使Python变成一种静态类型语言。唯一的使用场景就是保护代码不被无意义地调用。

在大多数情况下,健康的测试驱动开发(TDD)风格可以提供鲁棒性很好的基础代码。在这里,功能测试和单元测试验证了创建代码所针对的所有使用场景。

如果库中的代码被外部元素使用,那么进行断言可能是有用的,因为传入的数据可能会导致程序结束甚至造成破坏。这在处理数据库或文件系统的代码中可能发生。

另一种方法是模糊测试(fuzz testing,参见https://en.wikipedia.org/wiki/Fuzzing),它通过向程序发送随机的数据块来检测其弱点。如果发现了新的缺陷,代码会被修复以解决这一缺陷,并添加一次新的测试。

让我们来关注一个遵循TDD方法的代码库,它向正确的方向发展,每当出现新的缺陷时都会对其进行调整,从而鲁棒性越来越好。当它以正确的方式完成时,测试中的断言列表在某种程度上变得类似于先决条件列表。

4.3 小心使用*args**kwargs魔法参数

*args**kwargs参数可能会破坏函数或方法的鲁棒性。它们会使签名变得模糊,而且代码常常在不应该出现的地方构建小型的参数解析器,如下所示:

  def fuzzy_thing(**kwargs):

     if 'do_this' in kwarg
s:
          print('ok i di
d')

     if 'do_that' in kw
args:
          print('that is 
done')

     print('errr
... ok')

>>> fuzzy_thing(do_this=1)

  ok i did

 errr... ok

  >>> fuzzy_thi
ng(do_that=1)

 that is done

 errr... ok

  >>> fuzzy
_thing(hahaha=1)

  errr... ok

如果参数列表变得很长而且很复杂,那么添加魔法参数是很吸引人的。但这更表示它是一个脆弱的函数或方法,应该被分解或重构。

如果*args被用于处理元素序列(在函数中以相同方式处理),那么要求传入唯一的容器参数(例如iterator)会更好些,如下所示:

  def sum(*args):  # 可行
      total = 0
      for arg in args:
          total += arg
      return total
  def sum(sequence):  # 更好!
      total = 0
      for arg in sequence:
          total += arg
      return total

**kwargs适用于同样的规则。最好固定命名参数,使方法签名更有意义,如下所示:

  def make_sentence(**kwargs):
      noun = kwargs.get('noun', 'Bill')
      verb = kwargs.get('verb', 'is')
      adj = kwargs.get('adjective', 'happy')
      return '%s %s %s' % (noun, verb, adj)
  def make_sentence(noun='Bill', verb='is', adjective='happy'):
      return '%s %s %s' % (noun, verb, adjective)

另一种有趣的方法是创建一个容器类,将多个相关参数分组以提供执行上下文。这种结构与*args**kwargs不同,因为它可以提供能够操作数值并且能够独立发展的内部构件(internals)。使用它作为参数的代码将不必处理其内部构件。

例如,传入函数的Web请求通常由一个类实例表示。这个类负责保存Web服务器传入的数据,代码如下:

  def log_request(request):  # 版本1
      print(request.get('HTTP_REFERER', 'No referer'))
  def log_request(request):  # 版本2
      print(request.get('HTTP_REFERER', 'No referer'))
      print(request.get('HTTP_HOST', 'No host'))

魔法参数有时是无法避免的,特别是在元编程中。例如,想要创建能够处理任何类型签名的函数的装饰器,它是不可或缺的。更普遍地说,在处理对函数进行遍历的未知数据时,魔法参数都很好用,代码如下:

  import logging
  def log(**context):
      logging.info('Context is:\n%s\n' % str(context))

5 类的名称

类的名称必须简明、精确,并足以使人理解类的作用。常见的做法是使用后缀来表示其类型或特性。例如:

  • SQLEngine;
  • MimeTypes;
  • StringWidget;
  • TestCase。

对于基类或抽象类,可以使用一个BaseAbstract前缀,如下所示:

  • BaseCookie;
  • AbstractFormatter。

最重要的是要和类属性保持一致。例如,尽量避免类及其属性名称之间的冗余:

>>> SMTP.smtp_send()  # 命名空间中存在冗余信息
>>> SMTP.send()       # 

如果你也是看准了Python,想自学Python,在这里为大家准备了丰厚的免费学习大礼包,带大家一起学习,给大家剖析Python兼职、就业行情前景的这些事儿。

一、Python所有方向的学习路线

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

二、学习软件

工欲善其必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

三、全套PDF电子书

书籍的好处就在于权威和体系健全,刚开始学习的时候你可以只看视频或者听某个人讲课,但等你学完之后,你觉得你掌握了,这时候建议还是得去看一下书籍,看权威技术书籍也是每个程序员必经之路。

四、入门学习视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

五、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

成为一个Python程序员专家或许需要花费数年时间,但是打下坚实的基础只要几周就可以,如果你按照我提供的学习路线以及资料有意识地去实践,你就有很大可能成功!
最后祝你好运!!!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值