Add a new oop special method or enhance __getattr__ feature to handle the cases where an object does not invoke any attribute or method

Feature or enhancement

The main purpose is in native Python object-oriented programming, it can capture in real time and learn that an object does not invoke any attribute or method. The emphasis is on learning the situation synchronously and processing it when an object is invoked, rather than judging the situation through the connection of code context. Of course, the latter is completely impossible to achieve real-time in the latest Python version, because there is no special method that will be invoked simultaneously when the object is called, it is impossible to establish a real-time code context to determine whether the object invokes any attribute or method.

In the latest version of Python, I have tried my best to use Python native code to achieve the above requirements, but all failed. To this end I come up with the ideas of achieving the main purpose by enhancing Python itself, two alternative ways for solving the problem, and the Python core developers can choose either of the two solutions.

1.Add a new object-oriented programming special method

First I assume the name of this special method is __noinvokeattr__.

By default, its definition should be:

def __noinvokeattr__(self):
    pass

This special method will be called when the object does not invoke any other attribute or method. In actual application scenarios, this special method needs to be overridden to achieve some specific purposes of programming.

For example:

>>> class MyClass:
...     def __init__(self):
...         pass
...    
...     def __noinvokeattr__(self):
...         print("The Object has not invoked any attribute or method.")
...    
...     def method(self):
...    	    print("The object has invoked a method named 'method'.")
...
>>> obj = MyClass()
The Object has not invoked any attribute or method.
>>> obj
The Object has not invoked any attribute or method.
>>> obj.method()
The object has invoked a method named 'method'.

It is worth nothing that when the object is instantiated and assigned to obj for the first time, since no other non-special attribute or method is invoked, the __noinvokeattr__ is called.

Of course, we can also allow __noinvokeattr__ to receive the parameter of whether the object invokes special method to set whether to ignore other special methods to deal with certain specific situations during programming.

>>> class MyClass:
... 	def __init__(self):
... 		pass
...
... 	def __noinvokeattr__(self, is_invoke_special_method):
... 		if is_invoke_special_method:
... 			pass
... 		else:
...				print("The Object has not invoked any attribute or method.")
...		
... 	def __getattr__(self, item):
... 		print("The object doesn't have an attribute named '" + str(item) + "'.")
...
>>> obj = MyClass()

>>> obj.method()
The object doesn't have an attribute named method.

2.Enhance __getattr__ features which has been a special method in Python

The second approach also achieves the main purpose, and is similar to the first approach in terms of core operation logic design. However, it leverages the improvement and integration of Python’s existing special method to satisfy the requirements, instead of creating a new special method like __noinvokeattr__.

In the latest version of Python now for the following code:

>>> class MyClass:
... 	def __getattr__(self, item):
...			if item == None:
...				print("The Object has not invoked any attribute or method.")
...			if item:
... 			print("No attribute or method named '" + str(item) + "' exists in the 	...| 				object.")
...
>>> obj = MyClass()

>>> obj

>>> obj.method()
No attribute or method named 'method' exists in the object.

Obviously, if obj does not use dot notation, the __getattr__ method will not be called, let alone pass None to the formal parameter item.

The goal of enhancing __getattr__ features is to:

>>> class MyClass:
... 	def __getattr__(self, item):
...			if item == None:
...				print("The Object has not invoked any attribute or method.")
...			if item:
... 			print("No attribute or method named '" + str(item) + "' exists in the 	...| 				object.")
...
>>> obj = MyClass()
The Object has not invoked any attribute or method.
>>> obj
The Object has not invoked any attribute or method.
>>> obj.method()
No attribute or method named 'method' exists in the object.

After changing __getattr__ features, even if dot notation is not used, __getattr__ will be called and None will be passed to the formal parameter item when obj does not invoke any attribute or method.

Because __getattr__ itself is called when the object attribute or method cannot be found in the latest Python version, even if the existing special method is called, None will still be passed into the formal parameter item. This compares with the first approach in this regard why obj = MyClass() prints something despite calling __init__.

Pitch

Based on the above ideas, we can easily realize the selective acquisition of data within a specific range of the dynamic data dict.

The following example is MyClass().status stores a dynamic data dict about the status of the object, these dynamic data need to call the corresponding function to obtain in real time.

# Assuming this function takes 1 second to perform the calculation before returning the result.
def get_data_1():
    ...


# Assuming this function takes 1 minute to perform the calculation before returning the result.
def get_data_2():
    ...


# Assuming this function takes 1 hour to perform the calculation before returning the result.
def get_data_3():
    ...


class Dict(dict):  # A similar dict class stores dynamic data about the status of the MyClass class.
    def __get_value(self, key):
        if key == "data_1" or key == None:
            self["data_1"] = get_data_1()
        elif key == "data_2" or key == None:
            self["data_2"] = get_data_2()
        elif key == "data_3" or key == None:
            self["data_3"] = get_data_3()

    def __getattr__(self, item):
        if item == None:  # The object has not invoked any attribute or method.
            self.__get_value(None)  # Get all dynamic data.

    def __getitem__(self, item):
        self.__get_value(item)  # Get the specified dynamic data.


class MyClass:
    @property
    def status(self):
        return Dict()


obj = MyClass()

'''
In memory, the status dict only stores the data specified by dictionary key access operators -> obj.status = {"data_1":get_data_1()}.
obj.status["data_1"] = get_data_1()
'''
obj_status_data_1 = obj.status["data_1"]

'''
In memory, the status dict stores all data, since no data is specified by dictionary key access operators -> obj.status = {"data_1":get_data_1(), "data_2":get_data_2(), "data_3":get_data_3}.
'''
obj_status = obj.status    

The calculation time assumed in the comment before each function which is used to obtain data is to better magnify and highlight the characteristics of this dynamic data dict code design. The difference between using dictionary key access operators behind obj.status is whether it consumes more memory and time, which depends on the programming choice.

Just like the above code example, many are the current usage habit of dict:

The above code example The assignment object of the current dict Meaning in code example Spend time
obj_status = obj.status entire dict get all the data of the dynamic data dict more than an hour
obj_status_data_1 = obj.status["data_1"] the value corresponding to the key get specified data only one second

We can find that the code design of this dynamic data dict uses that feature to make a distinction. We seem to be able use __call__ to distinguish whether to obtain only the specified data, however, due to the preceding code syntax condition before calling __call__, it is destined that we will not be able to use the methods of dictionary operations after that. And that code design adheres to the concept of program optimization that does not load if not used, saves memory and time, and also caters to the programming habit of using dict .

Although in the latest Python version, we can inherit the dict class, and then override a series of related attributes and methods to achieve the same purpose (or when instantiating the dict object, first store the function pointer which is obtain data, and execute the specified function pointer when needed), but it will be very troublesome and difficult to finish it step by step. Therefore, adding that feature to native Python is similar to the utility of syntactic sugar, which I call code sugar. It’s a bit like adding the mechanism of the programming language itself to turn the tasks that need to be completed through repetitive or complex code into simple and fast completion, similar to the relationship between the characters array of C and the new std::string of CPP.

Much of what you write is not comprehensible to me (what do you mean by ‘native Python’?), but the specific idea strikes me as inappropriate for Python.

These both strike me as introducing bugs. In interactive mode, an expression by itself prints the representation of the resulting object. This is a) invaluable, and must not be broken and b) invokes obj.repr, making the masking statement wrong.

An assignment should not print anything. If one does want the assigned value echoed, add ‘;_’.

a = 3.3; _
3

I also don’t understand the English of this. It’s not just missing the use of typical Python jargon, it also doesn’t come close to conversational style grammar.

3 Likes