Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping
Recommended Course

Python vs Java: Object Oriented Programming
1h 17m · 16 lessons

Object-Oriented Programming in Python vs Java
Table of Contents
Recommended Course
Java programmers making a move to Python often struggle with Python’s approach to object-oriented programming (OOP). The approach to working with objects, variable types, and other language capabilities taken by Python vs Java are quite different. It can make switching between both languages very confusing.
This article compares and contrasts object-oriented programming support in Python vs Java. By the end, you’ll be able to apply your knowledge of object-oriented programming to Python, understand how to reinterpret your understanding of Java objects to Python, and use objects in a Pythonic way.
Over the course of this article, you’ll:
- Build a basic class in both Java and Python
- Explore how object attributes work in Python vs Java
- Compare and contrast Java methods and Python functions
- Discover inheritance and polymorphism mechanisms in both languages
- Investigate reflection across Python vs Java
- Apply everything in a complete class implementation in both languages
This article isn’t a primer on object-oriented programming. Rather, it compares object-oriented features and principles of Python vs Java. Readers should have good knowledge of Java, and also be familiar with coding Python. If you are unfamiliar with object-oriented programming, then check outIntro to Object-Oriented Programming (OOP) in Python. All Python examples will work with Python 3.6 or later.
Download Sample Code:Click here to download the commented sample object definitions and source code for the Java and Python objects in this article.
Sample Classes in Python vs Java
To begin, you’ll implement the same smallclass in both Python and Java to illustrate the differences between them. You’ll make modifications to them as the article progresses.
First, assume you have the followingCar class definition in Java:
1publicclassCar{ 2privateStringcolor; 3privateStringmodel; 4privateintyear; 5 6publicCar(Stringcolor,Stringmodel,intyear){ 7this.color=color; 8this.model=model; 9this.year=year;10}1112publicStringgetColor(){13returncolor;14}1516publicStringgetModel(){17returnmodel;18}1920publicintgetYear(){21returnyear;22}23}Java classes are defined in files with the same name as the class. So, you have to save this class in a file namedCar.java. Only one class can be defined in each file.
A similar smallCar class is written in Python as follows:
1classCar: 2def__init__(self,color,model,year): 3self.color=color 4self.model=model 5self.year=yearIn Python you can declare a class anywhere, in any file, at any time. Save this class in the filecar.py.
Using these classes as your base, you can explore the basic components of classes and objects.
Object Attributes
All object-oriented languages have some way to store data about the object. In Java and Python, data is stored inattributes, which are variables associated with specific objects.
One of the most significant differences between Python vs Java is how they define and manage class and object attributes. Some of these differences come from constraints imposed by the languages, while others come from best practices.
Declaration and Initialization
In Java, you declare attributes in the class body, outside of any methods, with a definite type. You must define class attributes before they are used:
1publicclassCar{ 2privateStringcolor; 3privateStringmodel; 4privateintyear;In Python, you both declare and define attributes inside the class__init__(), which is the equivalent of Java’s constructor:
1def__init__(self,color,model,year): 2self.color=color 3self.model=model 4self.year=yearBy prefixing the variable names withself, you tell Python these are attributes. Each instance of the class will have a copy. All variables in Python areloosely typed, and these attributes are no exception.
You can also create instance variables outside of.__init__(), but it’s not a best practice as their scope is often confusing. If not used properly, instance variables created outside of.__init__() can lead to subtle bugs that are hard to find. For example, you can add a new attribute.wheels to aCar object like this:
1>>>importcar 2>>>my_car=car.Car("yellow","beetle",1967) 3>>>print(f"My car is{my_car.color}") 4My car is yellow 5 6>>>my_car.wheels=5 7>>>print(f"Wheels:{my_car.wheels}") 8Wheels: 5However, if you forget themy_car.wheels = 5 on line 6, then Python displays an error:
1>>>importcar 2>>>my_car=car.Car("yellow","beetle",1967) 3>>>print(f"My car is{my_car.color}") 4My car is yellow 5 6>>>print(f"Wheels:{my_car.wheels}") 7Traceback (most recent call last): 8 File"<stdin>", line1, in<module> 9AttributeError:'Car' object has no attribute 'wheels'In Python, when you declare a variable outside of a method, it’s treated as a class variable. Update theCar class as follows:
1classCar: 2 3wheels=0 4 5def__init__(self,color,model,year): 6self.color=color 7self.model=model 8self.year=yearThis changes how you use the variablewheels. Instead of referring to it using an object, you refer to it using the class name:
1>>>importcar 2>>>my_car=car.Car("yellow","beetle",1967) 3>>>print(f"My car is{my_car.color}") 4My car is yellow 5 6>>>print(f"It has{car.Car.wheels} wheels") 7It has 0 wheels 8 9>>>print(f"It has{my_car.wheels} wheels")10It has 0 wheelsNote: In Python, you refer to a class variable using the following syntax:
- The name of the file containing the class, without the
.pyextension - A dot
- The name of the class
- A dot
- The name of the variable
Since you saved theCar class in the filecar.py, you refer to the class variablewheels on line 6 ascar.Car.wheels.
You can refer tomy_car.wheels orcar.Car.wheels, but be careful. Changing the value of the instance variablemy_car.wheels will not change the value of the class variablecar.Car.wheels:
1>>>fromcarimport* 2>>>my_car=car.Car("yellow","Beetle","1966") 3>>>my_other_car=car.Car("red","corvette","1999") 4 5>>>print(f"My car is{my_car.color}") 6My car is yellow 7>>>print(f"It has{my_car.wheels} wheels") 8It has 0 wheels 910>>>print(f"My other car is{my_other_car.color}")11My other car is red12>>>print(f"It has{my_other_car.wheels} wheels")13It has 0 wheels1415>>># Change the class variable value16...car.Car.wheels=41718>>>print(f"My car has{my_car.wheels} wheels")19My car has 4 wheels20>>>print(f"My other car has{my_other_car.wheels} wheels")21My other car has 4 wheels2223>>># Change the instance variable value for my_car24...my_car.wheels=52526>>>print(f"My car has{my_car.wheels} wheels")27My car has 5 wheels28>>>print(f"My other car has{my_other_car.wheels} wheels")29My other car has 4 wheelsYou define twoCar objects on lines 2 and 3:
my_carmy_other_car
At first, both of them have zero wheels. When you set the class variable usingcar.Car.wheels = 4 on line 16, both objects now have four wheels. However, when you set the instance variable usingmy_car.wheels = 5 on line 24, only that object is affected.
This means that there are now two different copies of thewheels attribute:
- A class variable that applies to all
Carobjects - A specific instance variable applicable to the
my_carobject only
It isn’t difficult to accidentally refer to the wrong one and introduce subtle bugs.
Java’s equivalent to a class attribute is astatic attribute:
publicclassCar{privateStringcolor;privateStringmodel;privateintyear;privatestaticintwheels;publicCar(Stringcolor,Stringmodel,intyear){this.color=color;this.model=model;this.year=year;}publicstaticintgetWheels(){returnwheels;}publicstaticvoidsetWheels(intcount){wheels=count;}}Normally, you refer to static variables using the Java class name. You can refer to a static variable through a class instance like Python, but it’s not a best practice.
Your Java class is getting long. One of the reasons why Java is more verbose than Python is the notion of public and private methods and attributes.
Public and Private
Java controls access to methods and attributes by differentiating betweenpublic data andprivate data.
In Java, it is expected that attributes are declared asprivate, orprotected if subclasses need direct access to them. This limits access to these attributes from code outside the class. To provide access toprivate attributes, you declarepublic methods which set and retrieve data in a controlled manner (more on that later).
Recall from your Java class above that thecolor variable was declared asprivate. Therefore, this Java code will show a compilation error at the highlighted line:
CarmyCar=newCar("blue","Ford",1972);// Paint the carmyCar.color="red";If you don’t specify an access level, then the attribute defaults topackage protected, which limits access to classes in the same package. You have to mark the attribute aspublic if you want this code to work.
However, declaring public attributes is not considered a best practice in Java. You’re expected to declare attributes asprivate, and usepublic access methods, such as the.getColor() and.getModel() shown in the code.
Python doesn’t have the same notion ofprivate orprotected data that Java does. Everything in Python ispublic. This code works with your existing Python class just fine:
>>>my_car=car.Car("blue","Ford",1972)>>># Paint the car...my_car.color="red"Instead ofprivate, Python has a notion of anon-public instance variable. Any variable which starts with anunderscore character is defined to be non-public. This naming convention makes it harder to access a variable, but it’s only a naming convention, and you can still access the variable directly.
Add the following line to your PythonCar class:
classCar:wheels=0def__init__(self,color,model,year):self.color=colorself.model=modelself.year=yearself._cupholders=6You can access the._cupholders variable directly:
>>>importcar>>>my_car=car.Car("yellow","Beetle","1969")>>>print(f"It was built in{my_car.year}")It was built in 1969>>>my_car.year=1966>>>print(f"It was built in{my_car.year}")It was built in 1966>>>print(f"It has{my_car._cupholders} cupholders.")It has 6 cupholders.Python lets you access._cupholders, but IDEs such asVS Code may issue a warning through linters that support PEP 8. For more on PEP 8, readHow to Write Beautiful Python Code With PEP 8.
Here’s the code in VS Code, with a warning highlighted and displayed:

Python further recognizes using double underscore characters in front of a variable to conceal an attribute in Python. When Python sees a double underscore variable, it changes the variable name internally to make it difficult to access directly. This mechanismavoids accidents but still doesn’t make data impossible to access.
To show this mechanism in action, change the PythonCar class again:
classCar:wheels=0def__init__(self,color,model,year):self.color=colorself.model=modelself.year=yearself.__cupholders=6Now, when you try to access the.__cupholders variable, you see the following error:
>>>importcar>>>my_car=car.Car("yellow","Beetle","1969")>>>print(f"It was built in{my_car.year}")It was built in 1969>>>my_car.year=1966>>>print(f"It was built in{my_car.year}")It was built in 1966>>>print(f"It has{my_car.__cupholders} cupholders.")Traceback (most recent call last): File"<stdin>", line1, in<module>AttributeError:'Car' object has no attribute '__cupholders'So why doesn’t the.__cupholders attribute exist?
When Python sees an attribute with double underscores, it changes the attribute by prefixing the original name of the attribute with an underscore, followed by the class name. To use the attribute directly, you need to change the name you use as well:
>>>print(f"It has{my_car._Car__cupholders} cupholders")It has 6 cupholdersWhen you use double underscores to conceal an attribute from the user, Python changes the name in a well-documented manner. This means that a determined developer can still access the attribute directly.
So if your Java attributes are declaredprivate, and your Python attributes are prefaced with double underscores, then how do you provide and control access to the data they store?
Access Control
In Java, you accessprivate attributes usingsetters and getters. To allow users to paint their cars, add the following code to your Java class:
publicStringgetColor(){returncolor;}publicvoidsetColor(Stringcolor){this.color=color;}Since.getColor() and.setColor() arepublic, anyone can call them to change or retrieve the car’s color. Java’s best practices of usingprivate attributes accessed withpublicgetters andsetters is one of the reasons why Java code tends to be more verbose than Python.
As you saw above, you access attributes directly in Python. Since everything ispublic, you can access anything at any time from anywhere. You set and get attribute values directly by referring to their names. You can even delete attributes in Python, which isn’t possible in Java:
>>>my_car=Car("yellow","beetle",1969)>>>print(f"My car was built in{my_car.year}")My car was built in 1969>>>my_car.year=1966>>>print(f"It was built in{my_car.year}")It was built in 1966>>>delmy_car.year>>>print(f"It was built in{my_car.year}")Traceback (most recent call last): File"<stdin>", line1, in<module>AttributeError:'Car' object has no attribute 'year'However, there are times you may want to control access to an attribute. In that case, you can use Python properties.
In Python,properties provide controllable access to class attributes using Python decorator syntax. (You can learn about decorators in the video coursePython Decorators 101.) Properties allow functions to be declared in Python classes that are analogous to Java getter and setter methods, with the added bonus of allowing you to delete attributes as well.
You can see how properties work by adding one to yourCar class:
1classCar: 2def__init__(self,color,model,year): 3self.color=color 4self.model=model 5self.year=year 6self._voltage=12 7 8@property 9defvoltage(self):10returnself._voltage1112@voltage.setter13defvoltage(self,volts):14print("Warning: this can cause problems!")15self._voltage=volts1617@voltage.deleter18defvoltage(self):19print("Warning: the radio will stop working!")20delself._voltageHere, you expand the notion of theCar to include electric vehicles. You declare the._voltage attribute to hold the battery voltage on line 6.
To provide controlled access, youdefine a function calledvoltage() to return the private value on lines 9 and 10. By using the@property decoration, you mark it as a getter that anyone can access directly.
Similarly, you define a setter function on lines 13 to 15, also calledvoltage(). However, you decorate this function with@voltage.setter. Lastly, you use@voltage.deleter to decorate a thirdvoltage() on lines 18 to 20, which allows controlled deletion of the attribute.
The names of the decorated functions are all the same, indicating they control access to the same attribute. The function names also become the name of the attribute you use to access the value. Here’s how these properties work in practice:
1>>>fromcarimport* 2>>>my_car=Car("yellow","beetle",1969) 3 4>>>print(f"My car uses{my_car.voltage} volts") 5My car uses 12 volts 6 7>>>my_car.voltage=6 8Warning: this can cause problems! 910>>>print(f"My car now uses{my_car.voltage} volts")11My car now uses 6 volts1213>>>delmy_car.voltage14Warning: the radio will stop working!Note that you use.voltage in the highlighted lines above, not._voltage. This tells Python to use the property functions you defined:
- When you print the value of
my_car.voltageon line 4, Python calls.voltage()decorated with@property. - When you assign a value to
my_car.voltageon line 7, Python calls.voltage()decorated with@voltage.setter. - When you delete
my_car.voltageon line 13, Python calls.voltage()decorated with@voltage.deleter.
The@property,@.setter, and@.deleter decorations make it possible to control access to attributes without requiring users to use different methods. You can even make attributes appear to be read-only properties by omitting the@.setter and@.deleter decorated functions.
self andthis
In Java, a class refers to itself with thethis reference:
publicvoidsetColor(Stringcolor){this.color=color;}this is implicit in Java code: it doesn’t normally need to be written, unless there may be confusion between two variables with the same name.
You can write the same setter this way:
publicvoidsetColor(StringnewColor){color=newColor;}SinceCar has an attribute named.color, and there isn’t another variable in scope with the same name, a reference to that name works. We usedthis in the first example to differentiate between the attribute and parameter both namedcolor.
In Python, thekeywordself serves a similar purpose. It’s how you refer to member variables, but unlike Java’sthis, it’srequired if you want to create or refer to a member attribute:
classCar:def__init__(self,color,model,year):self.color=colorself.model=modelself.year=yearself._voltage=12@propertydefvoltage(self):returnself._voltagePython requires eachself in the code above. Each one either creates or refers to the attributes. If you omit them, then Python will create a local variable instead of an attribute.
The difference between how you useself andthis in Python and Java is due to underlying differences between the two languages and how they name variables and attributes.
Methods and Functions
This difference between Python vs Java is, simply put, that Python has functions, and Java doesn’t.
In Python, the following code is perfectly fine (and very common):
>>>defsay_hi():...print("Hi!")...>>>say_hi()Hi!You can callsay_hi() from anywhere it’s visible. This function has no reference toself, which indicates that it’s a global function, not a class function. It can’t alter or store any data in any classes but can use local andglobal variables.
In contrast, every line of Java code you write belongs to a class. Functions can’t exist outside of a class, and by definition, all Java functions are methods. In Java, the closest you can get to a pure function is by using a static method:
publicclassUtils{staticvoidSayHi(){System.out.println("Hi!");}}Utils.SayHi() is callable from anywhere without first creating an instance ofUtils. Since you can callSayHi() without creating an object first, thethis reference doesn’t exist. However, this is still not a function in the sense thatsay_hi() is in Python.
Inheritance and Polymorphism
Inheritance andpolymorphism are two fundamental concepts in object-oriented programming.
Inheritance allows objects to derive attributes and functionality from other objects, creating a hierarchy moving from more general objects to more specific. For example, aCar and aBoat are both specific types ofVehicles. Objects can inherit their behavior from a single parent object or multiple parent objects, and are referred to as child objects when they do.
Polymorphism allows two or more objects to behave like one another, which allows them to be used interchangeably. For example, if a method or function knows how to paint aVehicle object, then it can also paint aCar orBoat object, since they inherit their data and behavior from theVehicle.
These fundamental OOP concepts are implemented quite differently in Python vs Java.
Inheritance
Python supports multiple inheritance, or creating classes that inherit behavior from more than one parent class.
To see how this works, update theCar class by breaking it into two categories, one for vehicles, and one for devices that use electricity:
classVehicle:def__init__(self,color,model):self.color=colorself.model=modelclassDevice:def__init__(self):self._voltage=12classCar(Vehicle,Device):def__init__(self,color,model,year):Vehicle.__init__(self,color,model)Device.__init__(self)self.year=year@propertydefvoltage(self):returnself._voltage@voltage.setterdefvoltage(self,volts):print("Warning: this can cause problems!")self._voltage=volts@voltage.deleterdefvoltage(self):print("Warning: the radio will stop working!")delself._voltageAVehicle is defined as having.color and.model attributes. Then, aDevice is defined to have a._voltage attribute. Since the originalCar object had these three attributes, it can be redefined to inherit both theVehicle andDevice classes. Thecolor,model, and_voltage attributes will be part of the newCar class.
In the.__init__() forCar, you call the.__init__() methods for both of the parent classes to make sure everything is initialized properly. Once done, you can add any other functionality you want to yourCar. In this case, you add a.year attribute specific toCar objects, and getter and setter methods for.voltage.
Functionally, the newCar class behaves as it always has. You create and useCar objects just as before:
>>>fromcarimport*>>>my_car=Car("yellow","beetle",1969)>>>print(f"My car is{my_car.color}")My car is yellow>>>print(f"My car uses{my_car.voltage} volts")My car uses 12 volts>>>my_car.voltage=6Warning: this can cause problems!>>>print(f"My car now uses{my_car.voltage} volts")My car now uses 6 voltsJava, on the other hand, only supports single inheritance, which means classes in Java can inherit data and behavior from only a single parent class. However, Java objects can inherit behavior from many differentinterfaces. Interfaces provide a group of related methods an object must implement, and allow multiple child classes to behave similarly.
To see this in action, split the JavaCar class into a parent class and aninterface:
publicclassVehicle{privateStringcolor;privateStringmodel;publicVehicle(Stringcolor,Stringmodel){this.color=color;this.model=model;}publicStringgetColor(){returncolor;}publicStringgetModel(){returnmodel;}}publicinterfaceDevice{intgetVoltage();}publicclassCarextendsVehicleimplementsDevice{privateintvoltage;privateintyear;publicCar(Stringcolor,Stringmodel,intyear){super(color,model);this.year=year;this.voltage=12;}@OverridepublicintgetVoltage(){returnvoltage;}publicintgetYear(){returnyear;}}Remember that eachclass andinterface needs to live in its own file.
As you did with Python, you create a new class calledVehicle to hold the more general vehicle related data and functionality. However, to add theDevice functionality, you need to create aninterface instead. Thisinterface defines a single method to return the voltage of theDevice.
Redefining theCar class requires you to inherit fromVehicle usingextend, and implement theDevice interface usingimplements. In the constructor, you call the parent class constructor using the built-insuper(). Since there is only one parent class, it can only refer to theVehicle constructor. To implement theinterface, you writegetVoltage() using the@Override annotation.
Rather than getting code reuse fromDevice as Python did, Java requires you to implement the same functionality in every class that implements theinterface. Interfaces only define the methods—they cannot define instance data or implementation details.
So why is this the case for Java? It all comes down to types.
Types and Polymorphism
Java’s strict type checking is what drives itsinterface design.
Everyclass andinterface in Java is a type. Therefore, if two Java objects implement the sameinterface, then they are considered to be the same type with respect to thatinterface. This mechanism allows different classes to be used interchangeably, which is the definition of polymorphism.
You can implement device charging for your Java objects by creating a.charge() that takes aDevice to charge. Any object that implements theDevice interface can be passed to.charge(). This also means that classes that do not implementDevice will generate a compilation error.
Create the following class in a file calledRhino.java:
publicclassRhino{}Now you can create a newMain.java to implement.charge() and explore howCar andRhino objects differ:
publicclassMain{publicstaticvoidcharge(Devicedevice){device.getVoltage();}publicstaticvoidmain(String[]args)throwsException{Carcar=newCar("yellow","beetle",1969);Rhinorhino=newRhino();charge(car);charge(rhino);}}Here is what you should see when you try to build this code:
Information:2019-02-02 15:20 - Compilation completed with 1 error and 0 warnings in 4 s 395 msMain.javaError:(43, 11) java: incompatible types: Rhino cannot be converted to DeviceSince theRhino class doesn’t implement theDevice interface, it can’t be passed into.charge().
In contrast to Java’s strict variable typing, Python uses a concept calledduck typing, which in basic terms means that if a variable “walks like a duck and quacks like a duck, then it’s a duck.” Instead of identifying objects by type, Python examines their behavior. You can learn more about the Python type system, and duck typing, inThe Ultimate Guide to Python Type Checking.
You can explore duck typing by implementing similar device charging capabilities for your PythonDevice class:
>>>defcharge(device):...ifhasattr(device,'_voltage'):...print(f"Charging a{device._voltage} volt device")...else:...print(f"I can't charge a{device.__class__.__name__}")...>>>classPhone(Device):...pass...>>>classRhino:...pass...>>>my_car=Car("yellow","Beetle","1966")>>>my_phone=Phone()>>>my_rhino=Rhino()>>>charge(my_car)Charging a 12 volt device>>>charge(my_phone)Charging a 12 volt device>>>charge(my_rhino)I can't charge a Rhinocharge() must check for the existence of the._voltage attribute in the object it’s passed. Since theDevice class defines this attribute, any class that inherits from it (such asCar andPhone) will have this attribute, and will therefore show they are charging properly. Classes that do not inherit fromDevice (likeRhino) may not have this attribute, and will not be able to charge (which is good, since charging rhinos can be dangerous).
Default Methods
All Java classes descend from theObject class, which contains a set of methods every other class inherits. Subclasses can either override them or keep the defaults. TheObject class defines the following methods:
classObject{booleanequals(Objectobj){...}inthashCode(){...}StringtoString(){...}}By default,equals() compares the addresses of the currentObject with a secondObject passed in, andhashcode() computes a unique identifier that also uses the address of the currentObject. These methods are used in many different contexts in Java. For example, utility classes, such as collections that sort objects based on value, need both of them.
toString() returns aString representation of theObject. By default, this is the name of the class and the address. This method is called automatically when anObject is passed to a method that requires aString argument, such asSystem.out.println():
Carcar=newCar("yellow","Beetle",1969);System.out.println(car);Running this code will use the default.toString() to show thecar object:
Car@61bbe9baNot very useful, right? You can improve this by overriding the default.toString(). Add this method to your JavaCar class:
publicStringtoString(){return"Car: "+getColor()+" : "+getModel()+" : "+getYear();}Now, when you run the same sample code, you’ll see the following:
Car: yellow : Beetle : 1969Python provides similar functionality with a set of commondunder (short for “double underscore”) methods. Every Python class inherits these methods, and you can override them to modify their behavior.
For string representations of an object, Python provides__repr__() and__str__(), which you can learn about inPythonic OOP String Conversion:__repr__ vs__str__. The unambiguous representation of an object is returned by__repr__(), while__str__() returns a human readable representation. These are roughly analogous to.hashcode() and.toString() in Java.
Like Java, Python provides default implementations of these dunder methods:
>>>my_car=Car("yellow","Beetle","1966")>>>print(repr(my_car))<car.Car object at 0x7fe4ca154f98>>>>print(str(my_car))<car.Car object at 0x7fe4ca154f98>You can improve this output by overriding.__str__(), adding this to your PythonCar class:
def__str__(self):returnf'Car{self.color} :{self.model} :{self.year}'This gives you a much nicer result:
>>>my_car=Car("yellow","Beetle","1966")>>>print(repr(my_car))<car.Car object at 0x7f09e9a7b630>>>>print(str(my_car))Car yellow : Beetle : 1966Overriding the dunder method gave us a more readable representation of yourCar. You may want to override the.__repr__() as well, as it is often useful for debugging.
Python offersa lot more dunder methods. Using dunder methods, you can define your object’s behavior during iteration, comparison, addition, or making an object callable directly, among other things.
Operator Overloading
Operator overloading refers to redefining how Python operators work when operating on user-defined objects. Python’s dunder methods allow you to implement operator overloading, something that Java doesn’t offer at all.
Modify your PythonCar class with the following additional dunder methods:
classCar:def__init__(self,color,model,year):self.color=colorself.model=modelself.year=yeardef__str__(self):returnf'Car{self.color} :{self.model} :{self.year}'def__eq__(self,other):returnself.year==other.yeardef__lt__(self,other):returnself.year<other.yeardef__add__(self,other):returnCar(self.color+other.color,self.model+other.model,int(self.year)+int(other.year))The table below shows the relationship between these dunder methods and the Python operators they represent:
| Dunder Method | Operator | Purpose |
|---|---|---|
__eq__ | == | Do theseCar objects have the same year? |
__lt__ | < | WhichCar is an earlier model? |
__add__ | + | Add twoCar objects in a nonsensical way |
When Python sees an expression containing objects, it calls any dunder methods defined that correspond to operators in the expression. The code below uses these new overloaded arithmetic operators on a couple ofCar objects:
>>>my_car=Car("yellow","Beetle","1966")>>>your_car=Car("red","Corvette","1967")>>>print(my_car<your_car)True>>>print(my_car>your_car)False>>>print(my_car==your_car)False>>>print(my_car+your_car)Car yellowred : BeetleCorvette : 3933There are many more operators you can overload usingdunder methods. They offer a way to enrich your object’s behavior in a way that Java’s common base class default methods don’t.
Reflection
Reflection refers to examining an object or class from within the object or class. Both Java and Python offer ways to explore and examine the attributes and methods in a class.
Examining an Object’s Type
Both languages have ways to test or check an object’s type.
In Python, you usetype() to display the type of a variable, andisinstance() to determine if a given variable is an instance or child of a specific class:
>>>my_car=Car("yellow","Beetle","1966")>>>print(type(my_car))<class 'car.Car'>>>>print(isinstance(my_car,Car))True>>>print(isinstance(my_car,Device))TrueIn Java, you query the object for its type using.getClass(), and use theinstanceof operator to check for a specific class:
Carcar=newCar("yellow","beetle",1969);System.out.println(car.getClass());System.out.println(carinstanceofCar);This code outputs the following:
class com.realpython.CartrueExamining an Object’s Attributes
In Python, you can view every attribute and function contained in any object (including all the dunder methods) usingdir(). To get the specific details of a given attribute or function, usegetattr():
>>>print(dir(my_car))['_Car__cupholders', '__add__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_voltage', 'color', 'model', 'voltage', 'wheels', 'year']>>>print(getattr(my_car,"__format__"))<built-in method __format__ of Car object at 0x7fb4c10f5438>Java has similar capabilities, but the language’s access control and type safety make it more complicated to retrieve.
.getFields() retrieves a list of all publicly accessible attributes. However, since none of the attributes ofCar arepublic, this code returns an empty array:
Field[]fields=car.getClass().getFields();Java treats attributes and methods as separate entities, sopublic methods are retrieved using.getDeclaredMethods(). Sincepublic attributes will have a corresponding.get method, one way to discover if a class contains a specific property might look like this:
- Use
.getDeclaredMethods()to generate an array of all the methods. - Loop through all the methods returned:
- For each method discovered, return true if the method:
- Begins with the word
getOR accepts zero arguments - AND doesn’t return
void - AND includes the name of the property
- Begins with the word
- Otherwise, return false.
- For each method discovered, return true if the method:
Here’s a quick-and-dirty example:
1publicstaticbooleangetProperty(Stringname,Objectobject)throwsException{ 2 3Method[]declaredMethods=object.getClass().getDeclaredMethods(); 4for(Methodmethod:declaredMethods){ 5if(isGetter(method)&& 6method.getName().toUpperCase().contains(name.toUpperCase())){ 7returntrue; 8} 9}10returnfalse;11}1213// Helper function to get if the method is a getter method14publicstaticbooleanisGetter(Methodmethod){15if((method.getName().startsWith("get")||16method.getParameterCount()==0)&&17!method.getReturnType().equals(void.class)){18returntrue;19}20returnfalse;21}getProperty() is your entry point. Call this with the name of an attribute and an object. It returnstrue if the property is found, andfalse if not.
Calling Methods Through Reflection
Both Java and Python provide mechanisms to call methods through reflection.
In the Java example above, instead of simply returningtrue if the property was found, you could call the method directly. Recall thatgetDeclaredMethods() returns an array ofMethod objects. TheMethod object itself has a method called.invoke(), which will call theMethod. Instead of returningtrue when the correct method is found on line 7 above, you can returnmethod.invoke(object) instead.
This capability exists in Python as well. However, since Python doesn’t differentiate between functions and attributes, you have to look specifically for entries that arecallable:
>>>formethod_nameindir(my_car):...ifcallable(getattr(my_car,method_name)):...print(method_name)...__add____class____delattr____dir____eq____format____ge____getattribute____gt____init____init_subclass____le____lt____ne____new____reduce____reduce_ex____repr____setattr____sizeof____str____subclasshook__Python methods are simpler to manage and call than in Java. Adding the() operator (and any required arguments) is all you need to do.
The code below will find an object’s.__str__() and call it through reflection:
>>>formethod_nameindir(my_car):...attr=getattr(my_car,method_name)...ifcallable(attr):...ifmethod_name=='__str__':...print(attr())...Car yellow : Beetle : 1966Here, every attribute returned bydir() is checked. You get the actual attribute object usinggetattr(), and check if it’s a callable function usingcallable(). If so, you then check if its name is__str__(), and then call it.
Conclusion
Throughout the course of this article, you learned how object-oriented principles differ in Python vs Java. As you read, you:
- Built a basic class in both Java and Python
- Explored how object attributes work in Python vs Java
- Compared and contrasted Java methods and Python functions
- Discovered inheritance and polymorphism mechanisms in both languages
- Investigated reflection across Python vs Java
- Applied everything in a complete class implementation in both languages
If you want to learn more about OOP in Python, be sure to readObject-Oriented Programming (OOP) in Python.
Understanding the differences in Python vs Java when handling objects, and the syntax choices each language makes, will help you apply best practices and make your next project smoother.
In order to compare some concrete examples side by side, you can click the box below to download our sample code to get the complete commented object definitions for the JavaCar,Device, andVehicle classes, as well as the complete commented definitions for the PythonCar andVehicle classes:
Download Sample Code:Click here to download the commented sample object definitions and source code for the Java and Python objects in this article.
Recommended Course
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

AboutJon Fincher
Jon taught Python and Java in two high schools in Washington State. Previously, he was a Program Manager at Microsoft.
» More about JonMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!
Keep Learning
Keep reading Real Python by creating a free account or signing in:
Already have an account?Sign-In




