来自Java程序员的Python OOP';s视角
我只有Java的OOP编程经验,刚刚开始用Python进行一个项目,我开始意识到Python让我对一些对我来说很直观的事情产生了怀疑。所以我有几个关于Python中OOP的问题 场景:我正在编写一个发送电子邮件的程序。对于电子邮件,将需要来自Java程序员的Python OOP';s视角,python,oop,Python,Oop,我只有Java的OOP编程经验,刚刚开始用Python进行一个项目,我开始意识到Python让我对一些对我来说很直观的事情产生了怀疑。所以我有几个关于Python中OOP的问题 场景:我正在编写一个发送电子邮件的程序。对于电子邮件,将需要收件人、发件人、文本和主题字段,其他字段如抄送和密件抄送将是可选的。此外,将有一组类实现核心邮件功能,因此它们将从基类(Mailer)派生 以下是我不完整的代码片段: class Mailer(object): __metaclass__ == abc.
收件人
、发件人
、文本
和主题
字段,其他字段如抄送
和密件抄送
将是可选的。此外,将有一组类实现核心邮件功能,因此它们将从基类(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
是。它是一个目录
还是一个列表
?另外,查看send_email
方法,也无法判断mailReq
是什么这是一种糟糕的编程实践吗?我应该做些别的事情吗?来自Java世界的我对编写代码感到非常不舒服,因为您无法通过查看代码来判断参数是什么。我了解了Python中的注释,但我不想使用它们,因为它们只在更高版本中受支持options
dict应该用于指定许多其他属性(cc
和bcc
只是其中的两个属性),因此我创建了一个名为MailRequestOptions
的新类,其中的所有选项都可以在options dict中指定为MailRequestOptions
的静态字符串这也是一种不好的做法,还是有更好的方法来做到这一点?我知道,这并不是Python特有的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
magic”使事情变得更简单(参见示例):
(请注意,中的在Python中是一个关键字,因此不能是参数的名称)
这样做的优点是可以合理地自我记录-有四个必需的参数,加上一些任意的额外关键字配置选项(同样,通常会记录在docstring中)
在Python中,不需要专门创建另一个对象。如果要包装邮件请求,可以使用字典:mailReq={'from':'johnsmith@british.com“,”到“:”…”,…}
您应该尝试使用*args
和**kwargs
作为方法。它可以使选项更简单:def send_mail(from,to,subject,text,**kwargs)
,然后可以使用例如kwargs['bcc']
检索其他选项。我相信这会更像蟒蛇
虽然jonrsharpe提供了一个极好的解决方案,但我认为值得一提的是我的方法
如前所述,在动态语言中,您不关心类型,只关心对象的接口(所谓的“duck”)。然后,您的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个参数,那么在某个时刻,您无法避免将它们全部传递给一个或多个分布式函数。您将它们分组多少次取决于您以及您在哪里找到平衡。这不是关于Python与Java,而是关于静态类型推断与动态类型推断:很好的解释。但是,作为这样定义的电子邮件的参数(from、to、subject、text、bbc和cc),我不会选择**kwargs
magic,而是在方法签名中指定它们。对于新手来说,它不那么令人困惑,而且更多的是自我记录。@ikaros45我认为OP的观点是,cc
和bcc
只是一些例子,有
class MailRequest(object):
def __init__(self, from_, to, subject, text, options=None):
"""
:type options: MailRequestOpts
"""
if options is None:
options = MailRequestOpts()
# ...
self.options = options