Python default behaviour

Hi all,

is it possible to change default Python execution/behaviour? For example, can I somehow change A>B to always return False?

Hello, @markoB, and welcome to the Python Forum!

When you define a type, you can include definitions concerning the actions of operators on that type.

See object.__gt__(self, other).

Also see operator — Standard operators as functions.

On that page, the information of particular interest concerning the > operator would be operator.__gt__(a, b).

EDITED (December 21, 2021) to add a link to the above

Hi @Quercus,
Thank you for the prompt answer!

I tried changing the file operator.py in the Python3xx/Lib folder, the function gt(a, b) to always return False, but nothing happened. Do I need to do something to propagate this change? Or maybe I made this change in the wrong place?

In order to help, we will need to see your code. Please supply us with it, properly formatted for posting. You can format code after you have copied and pasted it, either by selecting that code and clicking the </> button in the bar on top of the editing window, or by preceding it with a line of three backticks and following it with the same.

EDIT (December 21, 2021):

Here is a simple example:

class Falsification:
    def __init__(self, val):
        self.val = val
    def __gt__(self, other):
        # define the action of the > operator
        return False

fallacious_1 = Falsification(144)
fallacious_2 = Falsification(89)
print(fallacious_1 > fallacious_2)

Output:

False

Actually, it is quite a simple example. First, I changed the operator.py file (return statement has 4 whitespaces before the return command):

def gt(a, b):
    return False

I saved this file and started python.exe again.

And my code is:

import operator as o
o.gt(2,1)

According to my change of the gt function, it should return False, but it returns True. I hope this provides a better explanation for my question.

Note the double underscores in the name of the dunder method that defines the action of the operator here.

Ok, but I wasn’t able to find this method with double underscores, just this “plain” method without these underscores. But there is a statement
__gt__ = gt
near the end of this file.

Maybe it is worth mentioning, I’m using Python 3.10 version on Windows.

The ultimate goal is not clear to me. Can you accomplish what you are trying to do by subclassing an existing type?

Hello MarkoB,

No, you cannot change Python’s default behaviour.

Why would you want to make 10000 > 1 return False?

You should not make random changes to the Python standard library. That
is like opening the bonnet to your car’s engine and randomly undoing
nuts and unplugging wires and plugging them into different places, and
expecting it to work properly.

By changing the operator.gt function, you have now broken the operator
module, which means any other program, library or Python application
that uses the operator module will also be broken.

Presumably, this venture is purely for recreational purposes, since there is no obvious practical reason to make this change to the behavior of the Python interpreter. We seem to be in uncharted territory. Fortunately, with its being free, breaking the Python interpreter is less expensive than breaking the car engine. :grinning:

Less expensive except for those who are asked to debug some program when

the person asking for help has totally forgotten to mention that they

hacked parts of the standard library to do weird things, then ask why

Python is doing weird things.

1 Like

Hi Steven,

The reason for asking this question - since I’m no expert in Python, I wanted to find out where and how to change comparing functions in Python. Although it sounds crazy and illogical (I know :slight_smile: ), I wanted to ask this as simple as possible. But ok, let me give you a broader context.

The Python 2.x had no problem in comparing two objects, out of which one is None (for example number and None). Now I’m stuck with thousands of Python scripts, with comparison statements, relying on this way of behaviour. Occasionally one of the comparing objects is None, which results in a runtime error. Please, before suggesting, let me say - NO, I cannot alter those scripts and check if parameters are None or not. At least not in a reasonable period of time. So I got this wild idea, to alter the default behaviour of Python 3.x and allow it to compare objects like it did in version 2.x. As mentioned above, I need directions on where and how to do this, that’s why I asked for help. I wanted to avoid explaining the real reason behind it, so I asked how to change the default Python behaviour and posted this simple example “A > B”.

What I actually need, is to alter the default behaviour and somehow insert checking “is None” in these comparison functions. For example, if the gt function is “responsible” for checking (is greater than or >), instead of having:

def gt(a, b):
    "Same as a > b."
    return a > b

to have something like this:

def gt(a, b):
    "Same as a > b."

    if a is None:
        return False

    if b is None:
        return True

    return False

I hope this explains why I posted a question like this. :slight_smile:

Since Python is open source, I guess it is doable to alter the core functionalities and to achieve the upper explained behaviour, but it is a mystery to me how to do it.

On the other hand, if you have any other ideas on how to mitigate this “comparing with None” issue, I’m open to suggestions.

Thanks!

1 Like

Hi MarkoB,

You should have told us at the beginning what actual problem you were
really trying to solve.

No, operator.gt is not the function responsible for the behaviour of the
greater than > operator. There is no single source of truth for the
behaviour of the operator, due to operator overloading. Every object
gets a chance to say what the > operator means, using the special
methods __gt__ and __lt__.

The expression x > y translates to pseudo-code something like this:

# Untested! I may have the details wrong!
# Also, I have left out code that handles the case
# where one or the other special method is missing.

if y is a subclass of x:
    # Try the reflection first, y < x
    result = type(y).__lt__(x)
    if result is NotImplemented:
        result = type(x).__gt__(y)
else:
    result = type(x).__lt__(y)
    if result is NotImplemented:
        # Try the reflection y < x
        result = type(y).__lt__(x)

if result is NotImplemented:
    # Neither x nor y knows how to compare to the other.
    raise TypeError
else:
    return result

You could, in principle, hack the Python interpreter’s source code to
change the behaviour of None, and then rebuild the interpreter from
source. But what you get would not be Python any more, it would be a
hacked “almost Python” intepreter.

If you want to go down that path, you will need to be competant at C. Or
you might like to investigate Tauthon, the hybrid Python 2/3
interpreter.

I don’t know whether it keeps the Python 2 behaviour of comparisons with
None, or the Python 3 behaviour.

But ultimately, unless you want to stay with Python 2.7, or migrate to
Tauthon, you are going to need to adjust your code to either avoid
comparisons with None, or to use your own comparison function that
supports None.

And a lot of that depends on exactly what you are doing. If you are
writing code like:

x > y  # either of x or y may be None

then you can probably turn that into my_gt(x, y) after writing the
my_gt function. If you are sorting lists that contain both None and
(say) ints, you can sort None to the end with this key function:

lambda x: float('inf') if x is None else x

If you are sorting mixed None and str, you could try this key function:

lambda s: (0, '') if s is None else (1, s)

will sort all Nones to the beginning of the list.

1 Like

Thanks for the clarification regarding the goal of this project, @markoB, and for the detailed responses, @steven.daprano.

Thank you, @steven.daprano ,
Unfortunately, I can’t go that far and use some custom Python interpreter. It would be fine to change a file or two, but not the entire codebase of the interpreter. That’s why I asked about this operator.py.

Let me elaborate on my case a bit more. As I mentioned previously, I have thousands of Python scripts, which cannot be changed. For example, I have a script similar to this one (and nothing more than this function in that script):

def compareProps(c):
    if c.PropertyA > c.PropertyB:
        return c.PropertyA 
    else:
        return c.PropertyB

The only variation of these scripts are names of properties (PropertyA and PropertyB). Not to go any further into unnecessary details, later on, I’m calling this compare function from some other app (non-Python) and passing an object as parameter c. As I mentioned, sometimes a value of a property in that object is None. And NO, I can’t change the value of the property to something else than None before sending it to the function compareProps.

I could alter these scripts by concatenating some additional code before execution, but the existing compareProps functions in those scripts have to remain as they are.

In the described case, is there an option to somehow override the default behaviour for the “>” comparison? I tried “wrapping” in a class and adding another __gt__ method in that class (and adding “is None” check). But either I’m not doing it in the right way or it isn’t possible in the first place.

Could we see an example of that code?

Well, I tried several approaches, but mainly similar to this:

class A:
    def __init__(self, a):
        self.a = a

    def __gt__(self, other):
        return self<other #deliberately inverted the logic to see if it is working

    def compareObjects(c):
        #checking for None comes later, when proved it works
        if c.ObjectA > c.ObjectB:
            return c.ObjectA
        else:
            return c.ObjectB

Even if the upper case worked, I would have a dilemma about how to invoke it without changing the caller code in the main app. But, that is another issue.

Also, I would like to know if it is possible to add this __gt__ method override, by concatenating it with the existing compareObjects method and without using the class?

Sorry MarkoB, as I explained, there is no such thing as “the default
behaviour” for the > comparison operator, and no single place to
change it. There is no switch you can flip to give you the old
behaviour back.

Short of hacking the interpreter, there is no way to monkey-patch the
comparison operator for None or builtins like ints, floats and strings.

The only options are, in order of least work to most:

  • treat your code as legacy code, stuck on Python 2.7 forever;
  • run a custom interpreter, either your own or maybe Tauthon;
  • port your code to Python 3 by fixing the comparison functions;
  • rewrite the code completely in another language.

Why can’t you do the right thing and fix the comparison functions? Is it
that you don’t have the budget for it? I understand that it is a big
job, with thousands of files, but it looks like the modifications will
be pretty simple: just insert two lines in each function:

def compareProps(c):
    if c.PropertyA is None: return c.PropertyB
    if c.PropertyB is None: return c.PropertyA
    if c.PropertyA > c.PropertyB:
        return c.PropertyA 
    else:
        return c.PropertyB

I’m guessing you could modify each file in a minute or two. If there are
ten thousand files, that would take about 50 7-hour work days to fix for
one person. The work is pretty tedious and repetitive so you might need
to split the work up into smaller chunks (an hour or two at a time)
rather than full 7-hour blocks, otherwise you will tend to make more
mistakes.

Or you could script it: a good programmer would probably be able to take
a day to write a script that will run in an hour to fix all ten thousand
files.

All this assumes that the files are no more complicated than the example
you showed. If the code is more complicated than the sample file, then
it will take longer. Maybe much longer.

If you really, truly cannot change the files, and cannot hack the
interpreter or use another language, there are there is nothing to do
except treat them as legacy code stuck on Python 2.7 forever.

That’s okay, I know of at least two companies that have legacy code
running on Python 1.5. The code works and there is no business case for
spending the time and money to upgrade their applications to run in
Python 3, so they just run them in a VM using an ancient Python
interpreter.

Ok, I understand! But it was worth trying and I learned some new cool things. :slight_smile:

You are almost right, it should be simple and not take than a few days of work. But when you add those hours, plus testing per environment, plus several teams included, we get much, much bigger number. And that scenario I tried to avoid with this magic fix.

Thank you both, I really appreciate your time and help!