Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
来自Java程序员的Python OOP';s视角_Python_Oop - Fatal编程技术网

来自Java程序员的Python OOP';s视角

来自Java程序员的Python OOP';s视角,python,oop,Python,Oop,我只有Java的OOP编程经验,刚刚开始用Python进行一个项目,我开始意识到Python让我对一些对我来说很直观的事情产生了怀疑。所以我有几个关于Python中OOP的问题 场景:我正在编写一个发送电子邮件的程序。对于电子邮件,将需要收件人、发件人、文本和主题字段,其他字段如抄送和密件抄送将是可选的。此外,将有一组类实现核心邮件功能,因此它们将从基类(Mailer)派生 以下是我不完整的代码片段: class Mailer(object): __metaclass__ == abc.

我只有Java的OOP编程经验,刚刚开始用Python进行一个项目,我开始意识到Python让我对一些对我来说很直观的事情产生了怀疑。所以我有几个关于Python中OOP的问题

场景:我正在编写一个发送电子邮件的程序。对于电子邮件,将需要
收件人
发件人
文本
主题
字段,其他字段如
抄送
密件抄送
将是可选的。此外,将有一组类实现核心邮件功能,因此它们将从基类(
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特有的

  • 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