Python classes are slow

In the sense of Java and C ++, class member references are fast and map references are slow, but this is not always the case with Python. This is because Python treats class as a kind of dict internally, so even if you simply use dict in normal usage, it will not be so disadvantageous in terms of speed [^ 1].

[^ 1]: Since it was pointed out that it was necessary to measure the memory allocation and reference time separately, some measurement items were corrected. Part of the content has been changed accordingly.

Validation script


import collections
import random
import time


#
#Implemented in class.
#
class EClass:
	
	def __init__(self, x, y):
		self.x = x
		self.y = y

def test_class(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = EClass(x, y)
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implemented in class (with slots).
#
class EClassSlot:
	
	__slots__ = ["x", "y"]
	
	def __init__(self, x, y):
		self.x = x
		self.y = y

def test_class_slot(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = EClassSlot(x, y)
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implemented with dict.
#
def test_dict(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = {"x": x, "y": y}
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e["x"]
		y = e["y"]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implemented with list.
#
def test_list(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = [x, y]
		l.append(e)
	t2 = time.time()
	for e in l:
		x = e[0]
		y = e[1]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implemented with tuple.
#
def test_tuple(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = ((x, y, None))
		l.append(e)
	t2 = time.time()
	for i, e in enumerate(l):
		x = e[0]
		y = e[1]
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Implemented with namedtuple.
#
ENamedTuple = collections.namedtuple("E", ("x", "y"))

def test_namedtuple(n):
	l = []
	
	t1 = time.time()
	for _ in range(n):
		x = random.random()
		y = random.random()
		e = ENamedTuple(x, y)
		l.append(e)
	t2 = time.time()
	for i, e in enumerate(l):
		x = e.x
		y = e.y
		z = x + y
	t3 = time.time()
	
	return (t1, t2, t3)


#
#Conduct a test.
#
tests = [
	("class\t", test_class), 
	("class_slot", test_class_slot), 
	("dict\t", test_dict), 
	("list\t", test_list), 
	("tuple\t", test_tuple), 
	("namedtuple", test_namedtuple)
]

n = 5000000
print("test\t\talloc\trefer\ttotal")

for test, function in tests:
	t1, t2, t3 = function(n)
	print("%s\t%0.2f\t%0.2f\t%0.2f" 
		%(test, t2 - t1, t3 - t2, t3 - t1))

The results are shown below, and when focusing on the time required for the reference operation, it can be seen that the implementation using dict is not much different from the implementation using normal class. If you use slot, you can access it a little faster than dict, but object generation takes almost twice as long as dict and list, so if you consider including the time it takes to secure elements, this difference is about this. It becomes an almost meaningless number.

Execution result


test		alloc	refer	total
class		7.68	0.84	8.52
class_slot	6.41	0.76	7.17
dict		3.64	0.79	4.43
list		4.00	0.72	4.72
tuple		2.41	0.96	3.37
namedtuple	7.50	1.70	9.20

Although list is the fastest for reference operations, there is no reason to use it for the above purposes, considering the time and effort required to specify an element as an index. In fact, the tuple I was secretly expecting turned out not to be so fast in terms of reference speed. However, it is the fastest to secure the elements, and considering that the values cannot be changed after creation, it can be said that it is sufficient for exchanging small values.

namedtuple is very slow to generate and reference. This is because namedtuple searches for members in order from the beginning each time, and although memory efficiency is good, the speed becomes slower as the number of elements increases.

Recommended Posts

Python classes are slow
How python classes and magic methods are working.
Python basic course (13 classes)
When there are multiple Selenium SeleniumBasic classes (Python VBA)
About python objects and classes
Python classes learned in chemoinformatics
Paiza Python Primer 8: Understanding Classes
Python a + = b and a = a + b are different
Python> these are all considered False:
That Python code has no classes ...
Python ABC-Abstract Classes and Duck Typing
Python
Python: A Note About Classes 1 "Abstract"
What was surprising about Python classes
[Python] What are @classmethods and decorators?
[Python] About Executor and Future classes
Python classes and instances, instance methods
I hear that Matlab and Python loops are slow, but is that true?
Modules and packages in Python are "namespaces"
All Python arguments are passed by reference
Bind methods to Python classes and instances
Pharmaceutical company researchers summarized classes in Python
10 Python errors that are common to beginners
I looked back on Python classes again
Talking about old and new Python classes
PNG saving is slow with Python imageio.imwrite
Python open and io.open are the same