首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从Java程序员的角度看Python OOP

从Java程序员的角度看Python OOP
EN

Stack Overflow用户
提问于 2015-04-01 09:33:53
回答 3查看 442关注 0票数 5

我只拥有Java的OOP编程经验,并且刚刚开始使用Python开发一个项目,而且我开始意识到Python让我对一些对我来说直观的东西持怀疑态度。所以我有几个关于Python中OOP的问题。

场景:我正在编写一个程序,将发送电子邮件。对于电子邮件,tofromtextsubject字段是必需的,其他字段(如ccbcc )将是可选的。此外,还会有一组实现核心邮件功能的类,因此它们将从基类(Mailer)派生。

下面是我不完整的代码片段:

代码语言:javascript
复制
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"

问题:

  1. send_mail方法可以采用多个参数(from, to, subject, text, cc, bcc等)。在我的应用程序中只有四个是真正需要的。由于该方法的参数数量太多,我决定创建一个名为MailRequest的包装器对象,它将有四个必要的参数作为属性,所有其他参数都可以在options字典中定义。问题是,在这里,只要看一下代码,就没有办法说options是。它是dict还是list?此外,查看send_email方法,也无法判断mailReq是什么。,这是一个糟糕的编程实践吗?,我应该做别的什么吗?来自Java世界,只看代码就无法分辨参数的代码让我感到非常不舒服。我已经了解了Python中的注释,但我不想使用它们,因为只有在以后的版本中才支持它们。
  2. 由于应该使用options dict来指定许多其他属性(ccbcc只是其中的两个属性),所以我创建了一个名为MailRequestOptions的新类,所有选项都可以指定为MailRequestOptions的静态字符串。也是这种糟糕的实践,还是有更好的方法来做到这一点?我知道,并不是真正的特定于Python。
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-04-01 09:45:14

  1. 在Python中,不需要专门创建另一个对象。如果要包装邮件请求:mailReq = {'from': 'johnsmith@british.com', 'to': '....', ...},可以使用字典
  2. 您应该尝试对该方法使用*args**kwargs。它可以使选项变得更简单:def send_mail(from, to, subject, text, **kwargs),然后可以使用例如kwargs['bcc']检索其他选项。我相信这会更像毕多诺。
票数 4
EN

Stack Overflow用户

发布于 2015-04-01 09:41:50

Python是一种https://docs.python.org/2.7/glossary.html#term-duck-typing语言;如果它像鸭子一样走路,像鸭子一样嘎嘎叫,那就是鸭子!或者,在实现方面,如果作为mailReq传递的对象具有fromtosubjecttext属性,那么它是否是MailRequest并不重要。

如果您想记录这个接口(这当然是个好主意),那么使用文档字符串这样做是传统的做法。我喜欢谷歌风格,它可以与sphinx-napoleon一起使用来自动生成人类可读的文档,但其他文档是可用的。

代码语言:javascript
复制
def send_email(self, mailReq):
    """Send an email.

    Args:
      mailReq (MailRequest): the configuration for the email.

    """
    ...

至于第二个问题,将大量参数包装到容器对象中是一个相当常见的模式。在Python中,您可以选择使用"__**kwargs魔术“使事情变得简单一些(例如,参见(星号)参数做什么?):

代码语言:javascript
复制
def send_email(self, from_, to, subject, text, **config):
    ...
    bcc = config.get('bcc', [])  # option from config or a default
    ...

(请注意,from是Python中的关键字,因此不能作为参数的名称)。

这具有合理的自文档化的优点--有四个必需的参数,加上一些任意的附加关键字配置选项(同样,这些选项通常会记录在docstring中)。

票数 5
EN

Stack Overflow用户

发布于 2015-04-01 10:43:56

虽然jonrsharpe提供了一个很好的解决方案,但我认为值得一提的是我的方法。

如前所述,在动态语言中,您不关心类型,只关心对象具有的接口(所谓的“鸭子”)。然后,您的MailRequest对象是对逻辑上属于的参数进行分组的一种很好的方法。然而,它并没有实现它应该实现的一切。这将是我的做法:

代码语言:javascript
复制
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函数将如下所示:

代码语言:javascript
复制
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“构造函数”中的参数爆炸,那么解决方案就是更深层次地执行相同的操作:再次分组。例如,您可能希望将选项分组到它自己的类中:

代码语言:javascript
复制
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看起来是这样的:

代码语言:javascript
复制
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个参数,那么您无法避免在某个时候将它们全部传递给一个或多个分布式函数。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29387325

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档