【问题标题】:Python when to use instance vs static methodsPython 何时使用实例与静态方法
【发布时间】:2018-02-10 12:52:50
【问题描述】:

我很难理解何时使用实例方法与静态方法更有意义。另外,我不知道我的函数是否是静态的,因为没有 @staticmethod 装饰器。当我调用其中一种方法时,我可以访问类函数吗?

我正在开发一个将信息发送到数据库的网络爬虫。它设置为每周运行一次。我的代码结构是这样的

import libraries...
class Get:
    def build_url(url_paramater1, url_parameter2, request_date):
        return url_with_parameters

    def web_data(request_date, url_parameter1, url_parameter2): #no use of self
        # using parameters pull the variables to look up in the database
        for a in db_info:
            url = build_url(a, url_parameter2, request_date)
            x = requests.Session().get(url, proxies).json()
            #save data to the database
        return None

    #same type of function for pulling the web data from the database and parsing it

if __name__ == ‘__main__’:
    Get.web_data(request_date, url_parameter1, url_parameter2)
    Parse.web_data(get_date, parameter) #to illustrate the second part of the scrapper

这是基本结构。该代码是功能性的,但我不知道我是否正确使用了这些方法(函数?),并且可能会错过将来使用我的代码的方法。我什至可能正在编写糟糕的代码,这些代码会导致难以调试的错误,这仅仅是因为我没有遵循最佳实践。

在阅读了关于何时使用类和实例方法之后。我不明白为什么要使用它们。如果我想要构建 url 或从网站中提取数据,我调用 build_url 或 get_web_data 函数。我不需要函数的实例来单独跟踪任何内容。我无法想象我什么时候需要将我认为是问题的一部分的东西分开。

我认为我的问题与前面的问题不同的原因是:当我坐下来编写代码时,解释差异的概念示例似乎对我没有帮助。我还没有遇到过用不同方法解决的现实问题,这些方法显示了我什至应该使用实例方法,但在查看代码的概念示例时,实例方法似乎是强制性的。

谢谢!

【问题讨论】:

  • 静态方法在实践中很少见。它们根本不是真正的方法。它们只是碰巧存在于类命名空间而不是模块级别的常规函数​​。

标签: python static-methods instance-method


【解决方案1】:

这段代码根本不需要是一个类。它应该只是一对功能。您看不到为什么需要实例方法,因为您一开始就没有理由实例化对象。

【讨论】:

  • 在像我这样的例子中,我什么时候需要一个实例?这就是我正在努力解决的问题。我每周都在运行这些相同的功能,我可能让它变得比需要的复杂得多。
  • @DanielButler 你不会的。你没有课。如果你的“方法”有一个额外的self 参数作为它们的第一个参数,那么你需要定义一个类的无操作实例以调用方法:g = Get(); g.web_data(request_date, url_parameter1, url_parameter2)g 不包含任何数据,而您对 web_data 的定义确实使用了 self,这意味着 web_data 应该只是一个常规函数。由于相同的论点适用于build_url,并且您的课程没有任何其他方法,这意味着您的课程毫无意义。
  • @chepner 谢谢你的回复。我很难理解为什么我想要一个什么都不做的实例。回收部分类函数是不是会减少代码量?
  • 不会想要一个无所事事的实例;这就是为什么很多人告诉你首先定义两个函数而不是一个类的原因。您没有做任何需要,甚至可以使用类的事情。
【解决方案2】:

类可用于表示对象,也可用于在公共命名空间下对函数进行分组。

当一个类表示一个对象时,比如一只猫,这个对象“可以做”的任何事情,从逻辑上讲,都应该是一个实例方法,比如喵喵叫。

但是当你有一组相互关联或通常一起使用以实现共同目标的静态函数时,如build_urlweb_data,你可以让你的代码更清晰,更有条理:它们在一个静态类下,它提供一个公共命名空间,就像你做的那样。

因此,我认为您选择的结构是合法的。不过值得考虑的是,您会在更明确的 OOP 语言(如 Java)中找到更多静态类,而在 python 中,更常见的是使用模块来分隔命名空间。

【讨论】:

    【解决方案3】:

    您在代码中编写的函数是实例方法,但它们编写不正确。 实例方法必须self作为第一个参数

    def build_url(self, url_paramater1, url_parameter2, request_date):

    那你就这样称呼它

    get_inst = Get()
    get_inst.build_url(url_paramater1, url_parameter2, request_date)
    

    这个self 参数由python 提供,它允许您访问Get 类的所有属性和函数——无论是静态的还是非静态的。

    如果您不需要访问类中的其他函数或属性,则添加 @staticmethod 装饰器并删除 self 参数

    @staticmethod
    def build_url(url_paramater1, url_parameter2, request_date):
    

    然后就可以直接调用了

    Get.build_url(url_paramater1, url_parameter2, request_date)
    

    或从类实例调用

    get_inst = Get()
    get_inst.build_url(url_paramater1, url_parameter2, request_date)
    

    但是您可能会问,您当前的代码有什么问题? 尝试从这样的实例调用它,你会看到问题

    get_inst = Get()
    get_inst.build_url(url_paramater1, url_parameter2, request_date)
    

    创建实例有用的示例: 假设你想做一个聊天客户端。

    你可以这样写代码

    class Chat:
        def send(server_url, message):
            connection = connect(server_url)
            connection.write(message)
            connection.close()
    
        def read(server_url):
            connection = connect(server_url)
            message = connection.read()
            connection.close()
            return message
    

    但是一种更清洁、更好的方法:

    class Chat:
    
        def __init__(server_url):
            # Initialize connection only once when instance is created
            self.connection = connect(server_url)
    
        def __del__()
            # Close connection only once when instance is deleted
            self.connection.close()
    
        def send(self, message):
            self.connection.write(message)
    
        def read(self):
            return self.connection.read()
    

    要使用你做的最后一课

    # Create new instance and pass server_url as argument
    chat = Chat("http://example.com/chat")
    chat.send("Hello")
    chat.read()
    # deleting chat causes __del__ function to be called and connection be closed
    delete chat
    

    【讨论】:

    • Ramsay 感谢您抽出宝贵时间回复我的问题!是什么让创建实例然后调用函数优于我编写它的方式?只是大声思考,也许我的问题是我将我的代码视为应该调用的脚本或方法,而不是利用 python 提供的所有功能的全功能程序。
    • 我发现我的回复很长,所以我更新了我的答案。希望解释一下
    • 这更有意义,这就是我要找的。我不知道双下划线类型函数缺少什么。那是我需要集中学习的地方。我有一种感觉,我正在用蜡笔写作,以解决铅笔效果更好的问题。谢谢!
    • 很高兴能帮上忙,这里是所有__类函数docs.python.org/3/reference/datamodel.html#customization的文档
    【解决方案4】:

    从给定的示例中,毕竟没有必要拥有Get 类,因为您使用它就像一个额外的命名空间一样。在类或类实例中,您没有想要保留的任何“状态”。

    看起来不错的是有单独的模块并在其中定义这些功能。这样,在导入这个模块时,你就可以拥有你想要的这个命名空间。

    【讨论】:

    • 我一直在反复考虑使用计数器来跟踪我的网络拉取有用信息的想法。我的想法是计数器将是一个类变量,每次成功我 + 1。我不知道该怎么做。我将所有内容保存在具有不同类名称空间的同一个文件中,这样我就可以获取所有 Gets 并在同一个文件中解析它们。这可能不是你说的最好的方法。
    • 有多个用例可能支持将该计数器存储在类或类实例中。例如,如果您想为所有请求设置“全局”计数器,则使用类变量可能是解决方案(但不确定是否是最好的)。如果您想在多个上下文(例如每个域、每个请求者)中拥有相同数量的请求,则可以使用 counter 作为实例变量。这些只是想法,不一定是最好的。
    • 这是一个很好的例子,我没有想过使用类级别的计数器来计算某些条件。如果我要在一个类中创建一个函数(?)来区分我从中提取数据的网站,这是否有意义。一旦将类设置为网站,然后调用所有网站都相同的所有函数,如 get_web_data 和解析函数? (需要注意的是 parse web_data 真的是一样的)
    猜你喜欢
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    相关资源
    最近更新 更多