我只拥有Java的OOP编程经验,并且刚刚开始使用Python开发一个项目,而且我开始意识到Python让我对一些对我来说直观的东西持怀疑态度。所以我有几个关于Python中OOP的问题。
场景:我正在编写一个程序,将发送电子邮件。对于电子邮件,to、from、text和subject字段是必需的,其他字段(如cc和bcc )将是可选的。此外,还会有一组实现核心邮件功能的类,因此它们将从基类(Mailer)派生。
下面是我不完整的代码片段:
class Mailer(object):
__metaclass__ == abc.ABCMeta
def __init__(self,key):
self.key = key
@abc.abstractmethod
def send_email(self, mailReq):
pass
class MailGunMailer(Mailer):
def __init__(self,key):
super(MailGunMailer, self).__init__(key)
def send_email(self, mailReq):
from = mailReq.from
to = mailReq.to
subject= mailReq.subject
text = mailReq.text
options = getattr(mailReq,'options',None)
if(options != None):
if MailRequestOptions.BCC in options:
#use this property
pass
if MailRequestOptions.CC in options:
#use this property
pass
class MailRequest():
def __init__(self,from,to,subject,text):
self.from = from
self.to = to
self.subject = subject
self.text = text
def set_options(self,options):
self.options = options
class MailRequestOptions():
BCC = "bcc"
CC = "cc"问题:
send_mail方法可以采用多个参数(from, to, subject, text, cc, bcc等)。在我的应用程序中只有四个是真正需要的。由于该方法的参数数量太多,我决定创建一个名为MailRequest的包装器对象,它将有四个必要的参数作为属性,所有其他参数都可以在options字典中定义。问题是,在这里,只要看一下代码,就没有办法说options是。它是dict还是list?此外,查看send_email方法,也无法判断mailReq是什么。,这是一个糟糕的编程实践吗?,我应该做别的什么吗?来自Java世界,只看代码就无法分辨参数的代码让我感到非常不舒服。我已经了解了Python中的注释,但我不想使用它们,因为只有在以后的版本中才支持它们。options dict来指定许多其他属性(cc和bcc只是其中的两个属性),所以我创建了一个名为MailRequestOptions的新类,所有选项都可以指定为MailRequestOptions的静态字符串。也是这种糟糕的实践,还是有更好的方法来做到这一点?我知道,并不是真正的特定于Python。发布于 2015-04-01 09:45:14
mailReq = {'from': 'johnsmith@british.com', 'to': '....', ...},可以使用字典*args和**kwargs。它可以使选项变得更简单:def send_mail(from, to, subject, text, **kwargs),然后可以使用例如kwargs['bcc']检索其他选项。我相信这会更像毕多诺。发布于 2015-04-01 09:41:50
Python是一种https://docs.python.org/2.7/glossary.html#term-duck-typing语言;如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那就是鸭子!或者,在实现方面,如果作为mailReq传递的对象具有from、to、subject和text属性,那么它是否是MailRequest并不重要。
如果您想记录这个接口(这当然是个好主意),那么使用文档字符串这样做是传统的做法。我喜欢谷歌风格,它可以与sphinx-napoleon一起使用来自动生成人类可读的文档,但其他文档是可用的。
def send_email(self, mailReq):
"""Send an email.
Args:
mailReq (MailRequest): the configuration for the email.
"""
...至于第二个问题,将大量参数包装到容器对象中是一个相当常见的模式。在Python中,您可以选择使用"__**kwargs魔术“使事情变得简单一些(例如,参见(星号)参数做什么?):
def send_email(self, from_, to, subject, text, **config):
...
bcc = config.get('bcc', []) # option from config or a default
...(请注意,from是Python中的关键字,因此不能作为参数的名称)。
这具有合理的自文档化的优点--有四个必需的参数,加上一些任意的附加关键字配置选项(同样,这些选项通常会记录在docstring中)。
发布于 2015-04-01 10:43:56
虽然jonrsharpe提供了一个很好的解决方案,但我认为值得一提的是我的方法。
如前所述,在动态语言中,您不关心类型,只关心对象具有的接口(所谓的“鸭子”)。然后,您的MailRequest对象是对逻辑上属于的参数进行分组的一种很好的方法。然而,它并没有实现它应该实现的一切。这将是我的做法:
class MailRequest(object):
def __init__(self, from_, to, subject, text, bcc=None, cc=None):
# I am asuming a good default for bbc is an empty list. If none
# is fine, just remove the None checks.
# Dont get confused about this, it just avoids a pitfall with
# mutable default arguments. There are other techniques however
if bcc is None:
bcc = []
if cc is None:
cc = []
self.from_ = from_
self.to = to
self.subject = subject
self.text = text
self.bcc = bcc
self.cc = cc
# No options needed然后,send_email函数将如下所示:
def send_email(self, mailReq):
"""
:type mailReq: MailRequest
"""
from_ = mailReq.from_
to = mailReq.to
subject= mailReq.subject
text = mailReq.text
bcc = mailReq.bcc
cc = mailReq.cc注意,您只是记录了mailReq参数,指出传递的任何对象都应该提供MailRequest接口(至少部分)。通过这种方式,您可以将参数文档委托给MailRequest类。
比起**kwargs魔术,我更喜欢这种方法,因为参数在某个时候被显式地传递给了一个僵硬的签名,这在某种程度上充当了文档。缺点是冗长。
编辑
如果您害怕MailRequest“构造函数”中的参数爆炸,那么解决方案就是更深层次地执行相同的操作:再次分组。例如,您可能希望将选项分组到它自己的类中:
class MailRequestOpts(object):
def __init__(self, bbc=None, cc=None, colour=None, lights='blue', blink=True):
# ...
self.bbc = bbc
self.cc = cc
self.colour = colour
# etc...然后MailRequestClass看起来是这样的:
class MailRequest(object):
def __init__(self, from_, to, subject, text, options=None):
"""
:type options: MailRequestOpts
"""
if options is None:
options = MailRequestOpts()
# ...
self.options = options最后,如果一个进程需要50个参数,那么您无法避免在某个时候将它们全部传递给一个或多个分布式函数。
https://stackoverflow.com/questions/29387325
复制相似问题