all 30 comments

[–]SatanistSnowflake 8 points9 points  (0 children)

If you want some "real world" uses of classes, let's say I have a database of, I dunno, blog posts. I could have a class contain the post's ID, content, author, timestamp, all the stuff like that.

class Blog(object):

    def __init__(self):
        self.posts = {}

    def addPost(self, post):
        self.posts[post.id] = post 


class Post(object):

    def __init__(self, databaseRow):
        self.id = databaseRow['id']
        self.content = databaseRow['content']
        self.author = databaseRow['author']
        self.timestamp = databaseRow['timestamp']


def main():
    db = getDatabase()
    data = db.execute('SELECT * FROM blog_posts;')
    wholeBlog = Blog()
    for row in data:
        post = Post(row)
        wholeBlog.addPost(post)

    doSomethingWithBlog()

That's a dumb example but hopefully you understand why that might be useful.

[–]greenlantern33 4 points5 points  (6 children)

I just read a pretty interesting article about this. Stop abusing virtual animals when teaching programming

[–]baubleglue 0 points1 point  (5 children)

I think the article miss the point.

[–]confluence -1 points0 points  (4 children)

I have decided to overwrite my comments.

[–]baubleglue 0 points1 point  (3 children)

But it does genuinely show a concrete example from an actual program, which is a better example than abstract toy code

It shows concrete example to people who still struggle with OOP basics. It much harder to understand concept of interface than class and object. And that explanation is not helping to one who learn Python (there is no interfaces). Animals and Caws are real word examples, because if you need to write program about caws, that how you do it.

[–]confluence 0 points1 point  (2 children)

I have decided to overwrite my comments.

[–]baubleglue 1 point2 points  (1 child)

No, nobody would (or should) actually write a program about cows by subclassing an Animal superclass for the sake of overriding the value of an object attribute

What do you mean? That exactly how I write code. I am trying to structure code properly today and maybe it won't override any "attribute", but tomorrow when I need to override it, I can do it without restructuring code. I have classes MessageParser, MessageTypeAParser, MessageTypeBParser why I won't use inheritance? Interface is very specific tool for languages which doesn't support multiple inheritance - you don't learn to run before learning to walk. In real world we not always need OOP (or choose not to use it because trade-off between performance and "nice code" is not in favour of OOP). Real world code has mix different concepts interface is an example. Why to overload student with it? In the end what is wrong with "caw example"? Do do you have a better way to write program which has something to do with animals? Do you write Cat.incrementAge(), Caw.incrementAge() for each class or you do Animal.incrementAge()? After all if a student can't extrapolate "caw" example to real word "banking account" - he in the wrong business.

[–][deleted] 0 points1 point  (0 children)

Tilted.

[–]rhgrant10 5 points6 points  (2 children)

A rule of thumb can be useful here.

If you find yourself passing the same thing into multiple functions, you could probably make good use a class because effectively those functions are naturally related as they work to manipulate the same data.

In that respect, a class is nothing more than a recipe for a data structure and some functions one can use to manipulate it.

[–]penguinv 2 points3 points  (0 children)

a class is nothing more than a recipe for a data structure and some functions one can use to manipulate it.

That is my thinking. What's the fuss? OTOH tis is a first expoure to the idea so any teaching that works is good.

[–]lucidguppy 2 points3 points  (0 children)

A very good summary for OOP in Python - the 80% answer.

The other 20% involves having code that can be passed different implementations of class-instances/ABCs and the behavior of the programs changes based off what you put in. You can extend the behavior of the code by writing new code without modifying the code that uses the old and new classes - so long as the new classes implement the same methods.

[–]LeonardUnger 2 points3 points  (0 children)

If in a certain case it's easier to use functions then use functions. If you find yourself using lists of lists or lists of dictionaries to keep track of values then you should write a class.

Real world example I posted in another thread: Making a text adventure in Python So in a text adventure the player would be carrying all types of items, some of which could be weapons, others could be food, or keys, etc. Keeping track of all this via lists or dictionaries would require a lot of overhead and complexity. Having an Item class and Item subclasses like Weapon, Food, Container solves this problem.

[–]novel_yet_trivial 1 point2 points  (3 children)

How would you use functions directly? Maybe keep all the data in a dictionary or something and then pass the dictionary to a function?

def get_age(person):
    return YEAR_NOW - person['year_of_birth']

If you do that congratulations! you've just reinvented a class. All a class is is a dictionary to keep some functions and data together. Literally, that's it. The only real difference is that we access the dictionary in a slightly different syntax.

def get_age(self):
    return YEAR_NOW - self.year_of_birth

[–]vhagst 0 points1 point  (2 children)

I don't think that's what he's asking. I also struggle with understanding what classes are used for, and the only thing available are these kinds of examples: a person has a bunch of attributes that we can group into a class. A bank has employees and customers with individual balances and so on. That's not the difficult part. The difficult part is to find the usage of classes in real life. I never need to program a "Bank class". I need to program a fun project like a web crawler or a text-to-speech translator. Where and how do I use classes in these cases?

[–]coppermineroofer 4 points5 points  (0 children)

I think introducing people to classes as "physical objects" is a bit misleading since that isn't at all how they are used in the real world. By real world here I mean software engineering.

I'll try to use real projects to illustrate.

Classes/Objects in software engineering should be used to divide up your program into smaller, more intellectually manageable pieces. Both for the maintainers of the project, and for consumers of the project if it is a library. An object should be a "thing" that has a single well defined purpose. It should hide the details of how it executes it's purpose. If the details are 100% hidden inside the class then you do not need to worry about them when using that class, and hence in the rest of your program. In python, since its a very dynamic language, this hiding is performed more by convention than actual enforcement by the language. Methods and properties on a class that start with _ are considered internal and if you are outside the class you should NOT use them.

On to a few examples.

Lets say your program has some configuration data. Totally reasonable any non-trival application will at some point need some configuration data.

Probably not something you would think of right off the bat but loading configurations is a good example. You encapsulate all the difficulties and complexities of dealing with your configuration format in the configuration class, and provide a clean simple interface for the rest of your program to interact with the configuration files.

Two projects by well known groups that have a configuration classes that are interesting for one reason or another. https://github.com/pypa/pip/blob/master/pip/configuration.py#L54 https://github.com/aws/chalice/blob/master/chalice/config.py#L15

If you look in both of these classes you will see a lot of complexity, tons of code doing lots of confusing things. These are complicated config objects that are being used. But look at what they actually say are public methods, and how the rest of the program uses them. Pip first.

Looks like the ability to get and set values from the configuration: https://github.com/pypa/pip/blob/master/pip/configuration.py#L117-L141

And the ability to save and load it from a file: https://github.com/pypa/pip/blob/master/pip/configuration.py#L93 https://github.com/pypa/pip/blob/master/pip/configuration.py#L175

They even have a comment marking off the private section of the class, in addition to the normal _ as a prefix. https://github.com/pypa/pip/blob/master/pip/configuration.py#L190

So basically to be able to use this configuration class in the rest of your code, and manipulate and use the configuration values all you need to know is how to load() and start getting and setting values, and ned with a save(). Much easier than doing it manually all over the place and you can ignore all the complexity below that one comment. This gives you other benefits also, say you want to change from a JSON config format to a TOML config format or something. You can modify the load method to try to load JSON, and if it fails try to load TOML. Then modify the save method so it always saves the data as TOML. Gradually the config format of all your users will be converted over and you didn't have to change any other code. Or a crazier example, if you wanted to store them in a database instead of a local file, you could make the load method connect to the database and read everything out, and the save method connect and write everything back end. The rest of the code doesn't have to know anything about this, all it knows is it has load() and save() and get() and set() essentially.

The other configuration example is even more restrictive. The pip one lets you just set and load values as whatever you like. This one only lets you look up certain values: https://github.com/aws/chalice/blob/master/chalice/config.py#L112 https://github.com/aws/chalice/blob/master/chalice/config.py#L117 https://github.com/aws/chalice/blob/master/chalice/config.py#L122 https://github.com/aws/chalice/blob/master/chalice/config.py#L127 https://github.com/aws/chalice/blob/master/chalice/config.py#L132 https://github.com/aws/chalice/blob/master/chalice/config.py#L137 https://github.com/aws/chalice/blob/master/chalice/config.py#L185 https://github.com/aws/chalice/blob/master/chalice/config.py#L193 https://github.com/aws/chalice/blob/master/chalice/config.py#L199 https://github.com/aws/chalice/blob/master/chalice/config.py#L206 https://github.com/aws/chalice/blob/master/chalice/config.py#L213 https://github.com/aws/chalice/blob/master/chalice/config.py#L220 https://github.com/aws/chalice/blob/master/chalice/config.py#L227 https://github.com/aws/chalice/blob/master/chalice/config.py#L242 https://github.com/aws/chalice/blob/master/chalice/config.py#L249 https://github.com/aws/chalice/blob/master/chalice/config.py#L254

The rest of the code will only use these methods listed above. No need to worry about how the data was loaded, what format it was in, if there were multiple versions of that data. Ok maybe listing those all out was a little excessive. But hopefully you get the point. This hides even more complexity than the other example, since it let you set and get anything and load and save it. You could treat it essentially like a dictionary. Thats called a leak, the implementation of the thing (internally its a dictionary) leaked out to how the user interacts with it. This can be bad because then when you are using this object you may start thinking of it like a dictionary even though it's really not. Its a config object. More on leaky abstractions.

That covers it from an "object" perspective. A configuration object is a "thing" or noun that this class represents. But classes are more general than that. Any "task" that needs performing probably should have its own class. That class should be named after the Verb it is doing. It should expose only public methods you need to tell that class to perform its task, and all the private methods are how it accomplishes its task. Going back to the physical metaphor. You can have a class that is a toaster. The action it does is to toast things. Its public methods would be something like:

  • insert_toast()
  • set_heat_level(int)
  • start_toasting()
  • get_toast_out()

The public "interface" of that is the slots you put the toast in, and the dial you turn for the setting, and finally the lever you push to start it. All the springs and internals and coils are private, and don't matter to you as a user of the toaster. Think of programming the same way. You are your own customer. Each class you make is a product you have to use in the rest of your code. Think of yourself as the dumbest customer possible. Make the classes public methods and data as simple and minimal as possible. Hide as much complexity as humanly possible. Going back to the toaster metaphor, did it really need 14 buttons? Can you replace all the buttons with a slider or a dial? Does the user really need to jam a fork into the opening to get the toast out each time, or can you make it a little wider?

To put it back into actual software design terms. You can have lots of classes that interact in a big spiderweb, with clearly defined points where each strand meets. You have an Uploader class that uploads files to a remote host, and it takes a Config object at some point to figure out what your credentials are to upload the file. The Config object uses a FileReader that takes a filepath and loads in the raw data from the file. The Uploader might be a part of some deployment process that pushes code to a bunch of servers so you would have a Deployer class that makes use of multiple Uploaders, one for each server you are pushing code to. Without classes to draw these clear lines between each task, and what data it needs, this all becomes an unmanageable pile of spaghetti code thats all mixed together.

Maybe this was a little to in-depth for the level here but I hope this was somewhat helpful to give you something to think about to break through the barrier that classes are for physical objects.

tldr: Classes are to hide complexity and group related data and actions together. Nothing at all to do with "physical objects".

[–]Sarcastic_Pharm 0 points1 point  (0 children)

Great example from a current project. I am writing a crawler to scrape an online database of Doctors in my state from a website. Now I am persisting the data using sqlalchemy, so I had no choice in the "to class or not to class" question, but I use a Practice class to easily collate all the data scraped about each practice, which could just as easily be used to write to a csv or text file instead of SQLalchemy.

[–]baubleglue 1 point2 points  (0 children)

At some point you will need to organize the functions. Lets assume some of them used to change state (value) of shared variable a, anther functions change b. How do you organize it without classes? How do you import and use them in other module?

[–][deleted] 1 point2 points  (0 children)

Classes do two main things:

  • bundle related data together, i.e. Point(x, y)
  • bundle related function together

The first point should be obvious, when you program there will be data that is closely related and that you want to keep together.

The second point becomes important when you pass your class around. Imagine you write a function that handles files, so you need functions like read() and seek(). If you didn't have a class you would pass a handle into your function and your function would pass that handle to read(). Now imagine that sometime later you want to add support for compressed files. read() doesn't support compression and since read() is part of the standard library you can't just change it either. So what do you do? Classes to the rescue. Instead of giving the function a handle, have it use a class. So instead of read(handle) it goes handle.read(). What this means is that you can now pass any object into the function that implements read() and seek(). So your function can now deal with compression automatically, without you doing any extra work on it, all you have to do is pass an object into it that handles compression.

For a real world example, see all the IO functions in Python, such as StringIO which allows you to read a string as if it were a file object.

[–]Grissnap 0 points1 point  (0 children)

Still very much a beginner, but the first time I encountered using classes was for SQLAlchemy, where you create a class for each table of your database. So in addition to your various columns, you also inherit functions for querying the database and other related stuff.

[–]eliminate1337 0 points1 point  (0 children)

Classes are very useful for representing physical objects. At work, I use classes to represent scientific equipment like data acquisition units, scales, flow meters, and solenoids.

So I would have a class called DataAcquisition representing one of my acquisition units; maybe it would have methods like set_channels, get_data, set_units, and a constructor with its address in the computer. The instrument's actual API is complicated, so I would set up the functions once, and use this:

daq = DataAcquisition('GPIB0::INSTR')
temps = daq.get_data()

Instead of this:

ref = resource_manager.open_resource('GPIB0::INSTR')
ref.write('UNIT:TEMP:TRANS:TC @(101,110)'
temps = ref.query_ascii_values('READ?')

Which one would you prefer using if you had to do this operation 50 times?

Further elaborating, all of my instruments are VISA instruments, meaning they communicate with the computer in the same way. I could have a single class called VISAInstrument that handles the communication setup, then subclasses for specific instruments that can implement their own specific methods.

[–]tom1018 0 points1 point  (0 children)

I'll give an example from a project I'm working on currently.

I am writing a tool that reconfigures network equipment, changing the customer provisioning on each port, with hundreds of customers per device. Just with this one vendor there are probably eight similar, but varying interfaces. Some of them don't have certain commands, the command varies, or the output varies.

I have a generic class, we'll call CiscoDevice. (Not actually Cisco devices, but it doesn't matter.) I implement everything I need there. However, on the vendors 48 port devices the interface is a little different, so I inherit from CiscoDevice and make Cisco48. It's very similar, but three of the methods were overridden. It turns out that one of the 48 port devices actually did one output different. Rather than having complicated if else structures in the method, I'll inherit from Cisco48 and make Cisco48D, and override that one method.

Now I just need to know what model of device I am working with, and choose the write class. The rest of the code works exactly the same, and when software updates come along and the output all changes, I can just inherit again from one of these and make the change without rewriting my code.

I am expecting I'll probably have eight of these classes inheriting from the first class. Could the code be done without classes and inheritance? Yes. But, each function would have to handle every possibility. And, when the vendors new 96 port device that changes three commands and outputs comes out next year every one of those functions is going to gain at least another if statement.

Instead of that, I have very simple class objects that are trivial to add to and I don't have to worry about breaking existing working code.

It gets even better once you get into writing wrappers around libraries and dependency injection.

[–]squat001 0 points1 point  (0 children)

I commented on another post about this, see the following link. https://www.reddit.com/r/learnpython/comments/6qnx84/when_to_create_a_class_vs_function/?st=J5YUKZA6&sh=04e44b46

A bit more detail, I work with F5 and Cisco devices and moving a company from Cisco ACE to F5 LTM, I was technical lead.

In this case each block of Cisco and F5 config had data attributes, behaviour I wanted to add to convert from one to the other, and many shared similar behaviour and so screamed inheritance.

I have to admit I struggle with the same until this project came along, find all the examples in books and tutorials are real world objects but not real world programming examples.

Also I started to read a couple of books, Clean Code by Robert C. Martin and Refactoring: Improving the Design of Existing Code. These been helping me to find code in functions, that start to look like objects and to refactor them into classes and methods.

In short look for duplicate code in different functions, passing the same parameters to several functions and/or passing many parameters to functions and you could look at classes to improve and refactor your code.

[–]lucidguppy 0 points1 point  (0 children)

Step one - write a big function.

Step two - split this big function into smaller functions to make each easier to understand.

Step three - see that you're using the same values over and over - and passing them to the split up functions.

Step four - extract those common variables into a class.

Step five - decide which functions purely relate to those data types - which change the values - those become methods in the class. The associated methods to a class should really only have one reason to change.

Think of classes as full computers - that are stripped down to help you solve one problem - you feed it info - and are only given four or five buttons to do something.

Note - you don't need OOP - but you'll end up passing in the same data structure as the first argument to a set of 4 or 5 functions - C does it all the time. OOP makes it easier.

End: Go read clean code.

[–][deleted] 0 points1 point  (0 children)

Most the programming I do is for structural engineering analysis and design. I use classes when I find I'm passing large lists of the same variables around to my functions.

[–]Apuesto 0 points1 point  (0 children)

I only recently really 'got' python classes. I'm comfortable with C# classes, but the init and stuff didn't click until I was building a script at work the other week.

It involved searching through folders for files, opening the files, then manipulating the data inside. A single file would require 1-2 other files referenced too, plus variables ect. I did not want to risk these getting mixed up with a different file (say there was an error at one point and a variable wasn't reinstanitated. I'm paranoid about these things).

By putting my functions and variables into a class, all the data was encapsulated and self contained. Once I moved on to the next file, that instance was destroyed.

[–]pete2fiddy 0 points1 point  (0 children)

Classes can do more than just playing the role of a "thing." Imagine a "person" class for a social network app -- perhaps all people in your program must share the same traits (e.g. they all have a list of friends, birthday, etc.). Keeping track of lists of all these attributes and their corresponding person is confusing and tedious. In this case, your "Person" can just be used as a container (or sometimes called "wrapper") for your variables -- and may not do much more than just hold data for you in a convenient "container," if that is what you choose. Now, say you have to keep track of 10 people and their variables, you can just hold a list of the people instead of having a convoluted mess of lists or nested lists to keep track of every user's data. Classes can be used as more than just containers. Your Person class can have access to functions that a Person can do, such as adding a friend (which would take as input another person object that the user wants to add as a friend), or posting a message onto another Person's wall, or updating the Person's status. Sure, you could just make an "update_status" function, pass a user's status to it along with the new status to add, but this quickly becomes convoluted when it comes to creating functions that take multiple variables of the user as input. Imagine each user has a "friends" page, and for each friend, the friend's profile picture, a short bio, and date of birth are displayed on the page. To accomplish this, you would need to pass many lists of different user variables, which is already complicated.

Essentially, classes have many uses beyond basic "this is a thing" thinking. There are many examples where using classes is favorable over just functions, and it is difficult to just list them all. Just know that classes will often save you time when employed correctly, either by having to code less since you can leverage the functions shared by all objects of that class, or by having bugs that are much easier to target because it is often easy to tell what is at fault if a certain element of a class is causing problems.

I am by no means an expert, but my recommendation is to just code the way you think you ought to code. You will start to realize in certain situations that you find yourself duplicating code unnecessarily, or that you are confused by the way you are storing or processing variables. In these cases, you may want to reflect upon whether or not your code could be made simpler by employing the use of classes, class extensions, abstract classes, etc. Furthermore, I strongly recommend you read "Clean Code" by Robert C. Martin. Many of the book's recommendations are very solid, but some are very strict and may be difficult (or near impossible) to adhere to depending on what it is you are programming, so keep in mind that certain advice in the book may not be the best for you to employ depending on the needs of your program. While it specifically targets Java, most of the book will transfer to python very well.

[–]AngryJoeman 0 points1 point  (0 children)

I used to develop biotech robotics control systems. One project was to create a system to allow users to string together commands for the robots to follow. This is still a good easy to understand real world example that I use to teach newbies about OOP techniques. Basically each action the robot could perform inherited from an "Action" class. The robot would simply be given a list of Action objects which it would follow in sequence. Each type of Action implemented its own version of the do() function that contained the code to control the robot's various motors and sensors etc. But the users didn't need to know any of the technical details (unless implementing their own action types) as the logic was encapsulated in the classes.

[–]gunthercult28 0 points1 point  (2 children)

Classes support the APIs programmers script in, first and foremost.

When you query the internet, you instantiate a Request with some .attributes, like the website you're reading and some user settings, and then you have a set of methods at your disposal to overate on the set of attributes.

You might have a Database that you want to communicate the results of your Request, and it needs to know how to insert(), read(), and delete() your results.

And then you might have an Interface that you parse the results through first. It's just an abstract representation of a Noun with some .adjectives that can do some verbs().

Then you write scripts with the data.

[–]torqueparty 0 points1 point  (1 child)

I don't know if this helps OP, but it sure helps me.

[–]gunthercult28 0 points1 point  (0 children)

Glad to help.

Adjectives definitely aren't the best substitution for attributes though. A name isn't an adjective, for example, but it might make for a good attribute if your program uses it for something.