1

I am struggling to understand when it makes sense to use an instance method versus a static method. Also, I don't know if my functions are static since there is not a @staticmethod decorator. Would I be able to access the class functions when I make a call to one of the methods?

I am working on a webscraper that sends information to a database. It’s setup to run once a week. The structure of my code looks like this

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

That is the basic structure. The code is functional but I don’t know if I am using the methods (functions?) correctly and potentially missing out on ways to use my code in the future. I may even be writing bad code that will cause errors down the line that are impossibly hard to debug only because I didn’t follow best practices.

After reading about when class and instance methods are used. I cannot see why I would use them. If I want the url built or the data pulled from the website I call the build_url or get_web_data function. I don’t need an instance of the function to keep track of anything separate. I cannot imagine when I would need to keep something separate either which I think is part of the problem.

The reason I think my question is different than the previous questions is: the conceptual examples to explain the differences don't seem to help me when I am sitting down and writing code. I have not run into real world problems that are solved with the different methods that show when I should even use an instance method, yet instance methods seem to be mandatory when looking at conceptual examples of code.

Thank you!

Daniel Butler
  • 3,239
  • 2
  • 24
  • 37
  • 1
    Static methods are rare in practice. They aren't really methods at all; they are just regular functions that happen to exist in a class namespace rather than at the module level. – chepner Feb 10 '18 at 14:47

4 Answers4

3

Classes can be used to represent objects, and also to group functions under a common namespace.

When a class represents an object, like a cat, anything that this object 'can do', logically, should be an instance method, such as meowing.

But when you have a group of static functions that are all related to each other or are usually used together to achieve a common goal, like build_url and web_data, you can make your code clearer and more organized by putting them under a static class, which provides a common namespace, like you did.

Therefore in my opinion the structure you chose is legitimate. It is worth considering though, that you'd find static classes more in more definitively OOP languages, like Java, while in python it is more common to use modules for namespace separation.

kmaork
  • 5,722
  • 2
  • 23
  • 40
2

This code doesn't need to be a class at all. It should just be a pair of functions. You can't see why you would need an instance method because you don't have a reason to instantiate the object in the first place.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • In an example like mine when would I want an instance? That’s what I’m struggling with. I am running these same functions each week and I may have made it way more complicated than it needs to be. – Daniel Butler Feb 10 '18 at 13:26
  • @DanielButler You wouldn't. You don't have a class. If your "'methods" had an additional `self` parameter as the their first parameters, then you would need to define a do-nothing instance of the class in order to invoke the methods: `g = Get(); g.web_data(request_date, url_parameter1, url_parameter2)`. The fact that `g` contains no data and your definition of `web_data` does make any use of `self` means that `web_data` should just be a regular function. Since the same argument apples to `build_url`, and your class doesn't have any other methods, that means you class is pointless. – chepner Feb 10 '18 at 15:01
  • @chepner thank you for the reply. I am struggling to understand why I would want a do nothing instance. Would it be to reduce the amount of code if I am recycling parts of the class functions? – Daniel Butler Feb 10 '18 at 17:05
  • You *wouldn't* want a do-nothing instance; that's why multiple people have told you to just define two functions instead of a class in the first place. You aren't doing anything that requires, or can even make use of, a class. – chepner Feb 10 '18 at 17:49
2

The functions you have wrote in your code are instance methods but they were written incorrectly. An instance method must have self as first parameter

i.e def build_url(self, url_paramater1, url_parameter2, request_date):

Then you call it like that

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

This self parameter is provided by python and it allow you to access all properties and functions - static or not - of your Get class.

If you don't need to access other functions or properties in your class then you add @staticmethod decorator and remove self parameter

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

And then you can call it directly

Get.build_url(url_paramater1, url_parameter2, request_date)

or call from from class instance

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

But what is the problem with your current code you might ask? Try calling it from an instance like this and u will see the problem

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

Example where creating an instance is useful: Let's say you want to make a chat client.

You could write code like this

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

But a much cleaner and better way to do it:

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()

To use that last class you do

# 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
Ramast
  • 7,157
  • 3
  • 32
  • 32
  • Ramsay thank you for taking the time to reply to my question! What makes creating an instance then calling the function superior to the way I wrote it? Just thinking out loud, maybe my problem is I am thinking of my code as scripts or methods that should be called and not a fully functional program that utilizes all that python has to offer. – Daniel Butler Feb 10 '18 at 14:05
  • I found my reply to be very long so I updated my answer instead. Hope that explain it – Ramast Feb 10 '18 at 17:09
  • That makes a lot more sense and this is what I looking for. I didn't know what I missing with the double underscore type functions. That is where I need to focus my learning. I had a feeling I was writing with crayons to solve a problem where a pencil would work better. Thank you! – Daniel Butler Feb 10 '18 at 17:17
  • Glad to be of help, here is document for all `__` class functions https://docs.python.org/3/reference/datamodel.html#customization – Ramast Feb 10 '18 at 17:19
1

From given example, there is no need to have Get class after all, since you are using it just like a additional namespace. You do not have any 'state' that you want to preserve, in either class or class instance.

What seems like a good thing is to have separate module and define these functions in it. This way, when importing this module, you get to have this namespace that you want.

Ilija
  • 1,556
  • 1
  • 9
  • 12
  • I had been bouncing around the idea of using a counter to keep track of my web pulls with useful information. My thought was the counter would be a class variable that I + 1 with each success. I couldn’t figure out how to do it. I kept everything in the same file with different class name spaces so I could get all my Gets and parse them within the same file. That may not be the best way like you said. – Daniel Butler Feb 10 '18 at 13:17
  • There are multiple use cases that might be in favor of either storing that counter in class or in class instance. For example, if you want to have 'global' counter for all requests, having class variable might be the solution (not sure if the best one though). If you want to have like number of requests in more than one context (e.g. per domain, per requester), than you can use counter as instance variable. These are just ideas, not necessarily best ones. – Ilija Feb 10 '18 at 13:54
  • That’s a good example, I hadn’t thought of using a class level counter that would be used for counting certain conditions. Would it make sense if I were to create a function(?) inside a class to differentiate the website I am pulling the data from. Once the class is set to the website then call all of the functions that are the same for all websites like get_web_data and parse functions? (The caveat would be that parse web_data really is the same) – Daniel Butler Feb 10 '18 at 14:28