2

Attribute references and instantiation

In this link, that is part of the official Python documentation, I have found the following information:

Class objects support two kinds of operations: attribute references and instantiation.

Attribute references use the standard syntax used for all attribute references in Python. So if MyClass is the name of a class and func is the name of one attribute of MyClass then:
MyClass.func is a valid attribute reference.

Class instantiation uses function notation. So x = MyClass() creates a new instance of the class and assigns this object to the local variable x.

At the beginning of previous documentation the expression Class object is used and this means that in Python a class is an object and on this object we can execute only 2 operations and one of these is attribute references.

Up to now I have used the Attribute references only in one case to access a method of a class: for writing a unit-test which verified the exact sequence of method calls.

I show a simplified code of the test below (for details see here):

import unittest
from unittest import mock

class A:
    def __f1(self):
       pass
    def __f2(self):
       pass
    def __f3(self):
       pass
    def method_1(self):
       self.__f1()
       self.__f2()
       self.__f3()

class MyTestCase(unittest.TestCase):
    def test_call_order(self):
        mock_a = mock.create_autospec(A)
        expected = [mock.call._A__f1(),
                    mock.call._A__f2(),
                    mock.call._A__f3()]
        # call method_1 by reference
        A.method_1(mock_a)
        self.assertEqual(expected, mock_a.mock_calls)

if __name__ == '__main__':
    unittest.main()

I generally create an instance of a class and invoke the methods of the class by this instance object.
On my opinion it's better to use a python module which contains functions and global variables than create a class and access its attributes by reference.

My question

When or why can it be useful to access the attributes of a class by Attribute references and without an instance of that class? What is the point of this language feature? I have a hard time to imagine a case where attribute references make sense without an instance.

An example where it is absolutely necessary the use of such an attribute reference (as in my example) will be appreciated.

3
  • Have you any experience with static members in C++ / C# / Java?
    – Caleth
    Nov 9, 2023 at 16:14
  • I have used static methods in Java but I think that an abusing of them is not very compatible with OOP. In Python all methods are accessible by reference. So on my opinion this the same to use a module which contains functions instead a class.
    – User051209
    Nov 9, 2023 at 16:40
  • 4
    Dear community-hardliners, before downvoting on this question, think twice why you did not give any hints to the OP here on meta. Just downvoting a question with no comment where the OP explicitly asked for help is extremely unsocial behaviour.
    – Doc Brown
    Nov 9, 2023 at 17:16

2 Answers 2

5

That reference isn't describing a special case of the language rules, it's a natural result of everything being an object.

The special case is that MyClass(args...) is wired up to create a new object and call MyClass.__init__ (among other things).

It allows you to change the state of the class after it is defined. This can be as simple as changing "static" data based on some configuration.

class EggsApiClient:
    default_url = "example.com"
    def connect(self):
        # do stuff with default_url

if __name__ = "main":
    EggsApiClient.default_url = parse_args("default_url")
7
  • Thanks. Please change default_url: "example.com" to default_url = "example.com" in your answer. Very interesting yuor sentence it's a natural result of everything being an object.
    – User051209
    Nov 9, 2023 at 17:12
  • When I start to program with Python I think I abused in the use of objects and classes because I was coming from OOP in Java language. Now I think that in many cases I have to use a module and put inside global variables (instead attributes of a class) and functions (methods of a class). I'm convincing that to use modules, functions, global variables is more pythonic than use attribute reference.
    – User051209
    Nov 9, 2023 at 17:22
  • Very interesting your example MyClass.__init__ is what I'm looking for. Thank you very much you help me to better understand the hidden Python logic.
    – User051209
    Nov 9, 2023 at 17:44
  • 2
    I'm not sure this really addresses the question. Also, you can do similar things with class level variables in, e.g., Java even though classes are not objects.
    – JimmyJames
    Nov 9, 2023 at 18:11
  • 2
    @JimmyJames my point is there's a simplicity in Python's design here. Rather than having separate language features for each case, everything is an object, so everything can be read and written
    – Caleth
    Nov 10, 2023 at 9:01
3

This is not something that I think most Python users will ever need to do and almost surely shouldn't but it does have a distinct purpose and it's worth understanding.

Consider the following example:

class Foo:
    def set_a(self, a):
        self.a = a

class Bar:
    def set_b(self, b):
        self.b = b

foo = Foo()

foo.set_a(1)

print(foo.a)

bar = Bar()

# interesting part here!
Foo.set_a(bar, 2)

print(bar.a)

Through this feature, we have effectively called set_a on a Bar instance, despite the fact that Bar doesn't have a set_a method defined. That is, if we try:

bar.set_a()

We get an error: AttributeError: 'Bar' object has no attribute 'set_a'.

You might (quite reasonably) ask, "OK, but why would I do that?" Personally, I think I did something like this many moons ago but there was probably a simpler solution to whatever I was trying to accomplish.

I can imagine this be useful in various frameworks e.g. something like unit testing. But the main reason I think you might end up doing this is when you are using a more functional style. For example, you might have a function like this which has no knowledge of Foo or Bar:

def apply(obj, func, *params):
    func(obj, *params)

Which you can then call like so:

apply(bar, Bar.set_b, 3)

print(bar.b)

A real example of the kind of thing you might actually do:

class Person:
    def __init__(self, name: str, student: bool):
        self.name = name
        self.student = student

    def is_student(self):
        return self.student
    
    def __repr__(self):
        return f"{self.name} - student: {self.student}"
    
people = [Person("bob", False), Person("alice", True), Person("carl", True)]

def display(iter):
    print("---")
    for i in iter:
        print(i)


display(people)

display(filter(Person.is_student, people))

Or maybe something like this:

people = map(Person, ["dave", "edith", "frank"], [True, False, True])

display(people)

Check out the functools module for more interesting functional-style approaches like this.

9
  • 1
    Thank you very much. Your answer with the anwser of Caleth have added much to my my poor knowledge of Python. I agree with you that the use of attribute reference is inside framework or other special cases.
    – User051209
    Nov 9, 2023 at 18:20
  • My real difficult concern on the fact that sometimes I have to avoid to create class in Python and use module because classes are not so needed as for example in Java.
    – User051209
    Nov 9, 2023 at 18:22
  • 3
    @frankfalse I consider this to be intermediate to advanced Python knowledge. The fact that you are even asking suggests you may be underestimating yourself. My opinion is that with Python, you should use module level code as long as it meets your needs and isn't becoming spaghetti code and only introduce classes when they provide a clear benefit.
    – JimmyJames
    Nov 9, 2023 at 18:44
  • I agree with you and in the future I will use this approach to programming with the Python language. The code write in the past will remain as it is, but I think the use of Python class will decrease in my next new programs.
    – User051209
    Nov 10, 2023 at 8:34
  • The fact that you can do this in no way suggest you should. As to why it's possible requires an understanding of the descriptor protocol in Python and how it implements foo.set_a(1) as Foo.set_a(foo, 1) (via an intermediate call to Foo.set_a.__get__(foo, Foo)).
    – chepner
    Jan 23 at 21:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.