Python OO is not as succinct and pretty as Ruby. Java of course is wretched in every way and any comparison would be moot.
The following official description is very good and worth reading. Remember that everybody and every language has (apparently) different words for talking about the same concepts. Be aware and be flexible.
To get started here is a simple place holder for a class:
class Fish: passThe interesting thing here is the "pass" keyword which does nothing, but satisfies python syntax so this will compile. This can actually be useful, serving much the same as a C "struct" with members being added on the fly.
Here is a class with an initializer (constructor method):
class Dog: def __init__(self, name, age): self.name = name self.age = ageIf we write this code, we create an object of this class:
x = Dog ( "fido", 5 )This "Dog" object has instance variables (attributes) called name and age. We can introduce class variables (attributes) as follows:
class Fish: count = 0 def __init__(self, name): self.name = name Fish.count += 1Both instance and class variables are accessed the same way (via dot notation):
x = Fish ( "Barnie" ) print ( x.name ) print ( x.count )Note that we can add instance variables "willy-nilly" at any time to a single object instance via a line like:
x.abracadabra = "stop me if you can"There is some confusing (and I think broken) hokey pokey with accessing class versus instance variables. Inside the class self.count accesses the instance variable and Fish.count accesses the class variable. This is fine, and as it should be. If there is no "count" instance variable, accessing x.count outside the class yields the value of the class variable, which I find surprising. Accessing Fish.count outside gives the class variable with no confusion as it should. If we have both class and instance variables of the same name, accessing x.count gives the instance value (which makes sense), hiding the class variable. In this case we must use Fish.count to access the class variable.
My advice is to avoid this confusion and always use Fish.count to deal with class variables.
#!/bin/python3 class Fish: count = 0 def __init__(self, name): self.name = name self.count = 99 Fish.count += 1 def rename ( self, name ) : self.name = name x = Fish ( "joe" ) y = Fish ( "bob" ) print ( y.count ) print ( Fish.count ) y.rename ( "sam" ) print ( y.name )Note that the "self" vanishes outside the class when the method is called. It is magically injected as a first argument by python inside the class.
Along with instance methods, python also has class and static methods.
Class methods get a first argument magically injected by python, but it is "cls" rather than "self". This allows a class method to access class variables (class state).
Static methods don't get any magic first argument at all. So they can't access class variables, and of course they can't access instance variables.
Here is example code that would add a class and static method to our Fish class:
@classmethod def show ( cls ) : print ( f"cls: {cls.count}" ) @staticmethod def pork ( arg ) : print ( "static: " + arg )Note that the methods must be prefixed with a "decorator" that indicate that the method which follows is what it is. Don't be tricked into thinking that this is a comment. These two methods would be used in much the same way, as follows:
Fish.show() Fish.pork( "xyz" )Note that the only real reason to have static methods is to group plain old ordinary functions as part of a class, which is namespace control along with some logical grouping and organization.
class Cod ( Fish ) : passHere we have a "Cod" class with a parent class "Fish" that it inherits from. Enough said.
print ( x )We could add code like the following to our Fish class:
def __str__ ( self ) : return f"I am a fish, my name is {self.name}"We return a string that the print function will use. The f"" syntax is one of the ways python gives for printing formatted strings, something you can read about elsewhere.
There are dunder methods that help in setting up interators or generators.
Other dunder methods can set up infix operators like == or != for your class, indexing operations like [] and the usual gang of prefix and infix operators used for arithmetic (if you want to make your class really fancy).
A lot of powerful stuff lurks here, but I won't get into details.
Python does offer something stronger that was clearly added as a slapdash "patch" or afterthought. If you use two underscores, python replaces the name (invisibly) with "_classname__var". This clearly avoids namespace collisions in certain cases, but doesn't yield privacy.
nu = Fish.show nu()These are just objects and we can pass method objects around and place them in new variables. We can place them in lists or into other instance variables or whatever.
Giving the name "self" to the first argument of an instance method is just a convention. But it is a good one if you want others (or even yourself) to understand your code.
#!/bin/python3 class Fish: count = 0 def __init__(self, name): self.name = name self.count = 99 Fish.count += 1 def rename ( self, name ) : self.name = name def __str__ ( self ) : return f"I am a fish, my name is {self.name}" @classmethod def show ( cls ) : print ( f"cls: {cls.count}" ) @staticmethod def pork ( arg ) : print ( "static: " + arg ) x = Fish ( "joe" ) y = Fish ( "bob" ) print ( y.count ) print ( Fish.count ) y.rename ( "sam" ) print ( y.name ) print ( y ) Fish.pork( "xyz" ) Fish.show() nu = Fish.show nu()