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

2024-10-02 02:38:24 发布

您现在位置:Python中文网/ 问答频道 /正文

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

场景:我正在写一个可以发送电子邮件的程序。对于电子邮件,tofromtext和{}字段是必需的,cc和{}是可选的。另外,将有一组类将实现核心邮件功能,因此它们将从基类(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"

问题:

  1. send_mail方法可以接受多个参数(from, to, subject, text, cc, bcc,等等),在我的应用程序中只需要其中四个参数。由于该方法的参数数目太多,我决定创建一个名为MailRequest的包装器对象,它将有四个必要的参数作为属性,所有其他参数都可以在options字典中定义。问题是,在这里,仅仅看一下代码就不能说options是。是^{还是list?另外,看一下send_email方法,也没有办法知道mailReq是什么。这是一种糟糕的编程实践吗?我应该做点别的吗?来自Java世界,编写代码时,如果仅仅通过查看代码就无法判断参数是什么,这让我非常不舒服。我了解了Python中的注释,但我不想使用它们,因为它们只在以后的版本中受支持。

  2. 由于optionsdict应该用于指定许多其他属性(ccbcc只是其中的两个),我创建了一个新的类MailRequestOptions,其中所有的选项都可以在选项dict中指定为MailRequestOptions的静态字符串。这种做法也不好,还是有更好的方法?我知道这并不是Python特有的。


Tags: tokey代码textfromself参数init
3条回答
  1. 在Python中,不需要专门创建另一个对象。如果要包装邮件请求,可以使用字典:mailReq = {'from': 'johnsmith@british.com', 'to': '....', ...}

  2. 您应该尝试使用*args**kwargs作为方法。{cd4}可以更简单地使用其他选项。我相信这会更像Python。

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

如前所述,在动态语言中,您不关心类型,只关心对象具有的接口(所谓的“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函数如下所示:

^{pr2}$

注意,您只是记录了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是一种"duck-typed"语言;如果它走路像鸭子,嘎嘎叫得像鸭子,那它就是鸭子!或者,在实现方面,如果作为mailReq传递的对象具有fromtosubjecttext属性,它是否是MailRequest并不重要。在

如果您想记录接口(这当然是一个好主意),通常使用docstrings来完成。我喜欢the Google style,它可以与sphinx-napoleon一起使用,以自动生成人类可读的文档,但也可以使用其他文档。在

def send_email(self, mailReq):
    """Send an email.

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

    """
    ...

至于您的第二个问题:将大量参数包装到容器对象中是一个pretty common pattern。在Python中,您可以选择使用**kwargsmagic”(参见例如What does ** (double star) and * (star) do for parameters?):

^{pr2}$

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

这样做的好处是可以合理地自我文档化——有四个必需的参数,外加一些任意的附加关键字配置选项(同样,这通常会记录在docstring中)。在

相关问题 更多 >

    热门问题