Adding `__getattr__` method to an existing object instance [duplicate] - python

This question already has answers here:
Overriding special methods on an instance
(5 answers)
Closed 3 years ago.
I would like this to work:
import types
def new_getattr(self, *args, **kwargs):
return 2
class A:
def __init__(self):
pass
a = A()
a.__getattr__ = types.MethodType(new_getattr, a)
print(a.anything)
Right now, it throws AttributeError: A instance has no attribute 'anything'.
I tried different solutions proposed here and they work, but not for __getattr__.
If I do print(a.__getattr__('anything')), it actually prints 2; the problem is that my __getattr__ method is not called automatically when I do a.anything.
As a side note, in my actual implementation, I cannot modify the definition of the class A, nor can I type its name and do something like A.__getattr__ = ... (which would work) because I need this to be generic and independent of the class name.
Edit: I ended up doing it like this:
a.__class__.__getattr__ = new_getattr.

You can not - __dunder__ names are resolved on the type, not per-instance. Custom __getattr__ will need to be defined directly on A.
See Special method lookup section of the datamodel documentation, specifically:
For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.
Note: if you only have a reference to an instance, not the class, it is still possible to monkeypatch the type by assigning a method onto the object returned by type(a). Be warned that this will affect all existing instances, not just the a instance.

Related

How to access args of generic class [duplicate]

This question already has an answer here:
Access type argument in any specific subclass of user-defined Generic[T] class
(1 answer)
Closed 4 months ago.
If I have a class A:
T = TypeVar("T")
class A(Generic[T]):
a: T
How do I access the Generic[T] with the type-object A
typing.get_origin(A[...]).__bases__ just returns a <class 'typing.Generic'> instead of typing.Generic[~T]
You are looking for __orig_bases__. That is set by the type metaclass when a new class is created. It is mentioned here in PEP 560, but is otherwise hardly documented.
This attribute contains (as the name suggests) the original bases as they were passed to the metaclass constructor in the form of a tuple. This distinguishes it from __bases__, which contains the already resolved bases as returned by types.resolve_bases.
Here is a working example:
from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
a: T
class B(A[int]):
pass
print(A.__orig_bases__) # (typing.Generic[~T],)
print(B.__orig_bases__) # (__main__.A[int],)
Since it is poorly documented, I would be careful, where you use it. If you add more context to your question, maybe we'll find a better way to accomplish what you are after.
Possibly related or of interest:
Access type argument in any specific subclass of user-defined Generic[T] class

why do we pass self on all class methods? [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 1 year ago.
I know that some of you will think It's a stupid question but I will ask anyway.
why do we need to pass 'self' on all class methods can't we use it without passing it like this:
class Player:
def __init__(name):
self.name = name
def print_player_name():
print(self.name)
Try it. It won't work because self is not defined. The name "self" is just established, but you could name it whatever you want. It refers to the object of the class on which the method is called.
Yes, you can do it but you have to mention it as a static method. Self represents the instance of the class. In simple words, self represents the object on which it is being executed. When you create an object, all variables change their values based on different object of same class. Every class needs object as it's argument because for different object, different values are assigned
self represents object of class on which method is called you don't always need to name it self[standard convention] any valid variable name is allowed in python to do this but it should be first argument of non-classmethods and should be replace self as in if you run this python will work as expected :
class Player:
def __init__(hello, name):
hello.name = name
def print_player_name(hello):
print(hello.name)
It's very simple that's the official Python convention. In official docs we can read about it.
"Often, the first argument of a method is called self. This is nothing
more than a convention: the name self has absolutely no special
meaning to Python. Note, however, that by not following the convention
your code may be less readable to other Python programmers, and it is
also conceivable that a class browser program might be written that
relies upon such a convention."

Getting private attribute in parent class using super(), outside of a method

I have a class with a private constant _BAR = object().
In a child class, outside of a method (no access to self), I want to refer to _BAR.
Here is a contrived example:
class Foo:
_BAR = object()
def __init__(self, bar: object = _BAR):
...
class DFoo(Foo):
"""Child class where I want to access private class variable from parent."""
def __init__(self, baz: object = super()._BAR):
super().__init__(baz)
Unfortunately, this doesn't work. One gets an error: RuntimeError: super(): no arguments
Is there a way to use super outside of a method to get a parent class attribute?
The workaround is to use Foo._BAR, I am wondering though if one can use super to solve this problem.
Inside of DFoo, you cannot refer to Foo._BAR without referring to Foo. Python variables are searched in the local, enclosing, global and built-in scopes (and in this order, it is the so called LEGB rule) and _BAR is not present in any of them.
Let's ignore an explicit Foo._BAR.
Further, it gets inherited: DFoo._BAR will be looked up first in DFoo, and when not found, in Foo.
What other means are there to get the Foo reference? Foo is a base class of DFoo. Can we use this relationship? Yes and no. Yes at execution time and no at definition time.
The problem is when the DFoo is being defined, it does not exist yet. We have no start point to start following the inheritance chain. This rules out an indirect reference (DFoo -> Foo) in a def method(self, ....): line and in a class attribute _DBAR = _BAR.
It is possible to work around this limitation using a class decorator. Define the class and then modify it:
def deco(cls):
cls._BAR = cls.__mro__[1]._BAR * 2 # __mro__[0] is the class itself
return cls
class Foo:
_BAR = 10
#deco
class DFoo(Foo):
pass
print(Foo._BAR, DFoo._BAR) # 10 20
Similar effect can be achieved with a metaclass.
The last option to get a reference to Foo is at execution time. We have the object self, its type is DFoo, and its parent type is Foo and there exists the _BAR. The well known super() is a shortcut to get the parent.
I have assumed only one base class for simplicity. If there were several base classes, super() returns only one of them. The example class decorator does the same. To understand how several bases are sorted to a sequence, see how the MRO works (Method Resolution Order).
My final thought is that I could not think up a use-case where such access as in the question would be required.
Short answer: you can't !
I'm not going into much details about super class itself here. (I've written a pure Python implementation in this gist if you like to read.)
But now let's see how we can call super:
1- Without arguments:
From PEP 3135:
This PEP proposes syntactic sugar for use of the super type to
automatically construct instances of the super type binding to the
class that a method was defined in, and the instance (or class object
for classmethods) that the method is currently acting upon.
The new syntax:
super()
is equivalent to:
super(__class__, <firstarg>)
...and <firstarg> is the first parameter of the method
So this is not an option because you don't have access to the "instance".
(Body of the function/methods is not executed unless it gets called, so no problem if DFoo doesn't exist yet inside the method definition)
2- super(type, instance)
From documentation:
The zero argument form only works inside a class definition, as the
compiler fills in the necessary details to correctly retrieve the
class being defined, as well as accessing the current instance for
ordinary methods.
What were those necessary details mentioned above? A "type" and A "instance":
We can't pass neither "instance" nor "type" which is DFoo here. The first one is because it's not inside the method so we don't have access to instance(self). Second one is DFoo itself. By the time the body of the DFoo class is being executed there is no reference to DFoo, it doesn't exist yet. The body of the class is executed inside a namespace which is a dictionary. After that a new instance of type type which is here named DFoo is created using that populated dictionary and added to the global namespaces. That's what class keyword roughly does in its simple form.
3- super(type, type):
If the second argument is a type, issubclass(type2, type) must be
true
Same reason mentioned in above about accessing the DFoo.
4- super(type):
If the second argument is omitted, the super object returned is
unbound.
If you have an unbound super object you can't do lookup(unless for the super object's attributes itself). Remember super() object is a descriptor. You can turn an unbound object to a bound object by calling __get__ and passing the instance:
class A:
a = 1
class B(A):
pass
class C(B):
sup = super(B)
try:
sup.a
except AttributeError as e:
print(e) # 'super' object has no attribute 'a'
obj = C()
print(obj.sup.a) # 1
obj.sup automatically calls the __get__.
And again same reason about accessing DFoo type mentioned above, nothing changed. Just added for records. These are the ways how we can call super.

Can python class reference nonexistent variable and method? [duplicate]

This question already has answers here:
What is a mixin and why is it useful?
(18 answers)
Closed 2 years ago.
class SoftDeleteMixin(object):
deleted_at = Column(DateTime)
deleted = Column(types.SoftDeleteInteger, default=0)
def soft_delete(self, session):
"""Mark this object as deleted."""
self.deleted = self.id
self.deleted_at = timeutils.utcnow()
self.save(session=session)
In class SoftDeleteMixin method soft_delete, it references nonexistent self.id and self.save. Why can it do that in python?
Note: the focus is the class can reference nonexistent variable and method, not that it is a Mixin class.
If you instantiate a SoftDeleteMixin class and call the soft_delete method, you'll get an AttributeError.
If as you said in the comment those attributes are instantiated somewhere else, even in a child class, and you call soft_delete on a child class instance, it works because the attribute is there at the time the method is called.
To explain it in a simple way, python is an interpreted language, and except for syntax it does not perform too much checks on the whole file when executing the code, until that actual line is actually executed.
So yes, you could think it's a bad design but it is not, it's an accepted practice (see this question for more details) and it is allowed by the laguage. You can define methods which reference attributes not defined in a __init__ method or as class attributes or whatever. The important thing is that the istance has the attribute when the method is executed. It does not matter where or when the attribute is actually defined.
The word "mixin" in the class name means that this class is intended to be inherited by a class that already declares id and save(). If you try to use it by itself, it will cause errors.

Questions related to classes

I have a problem understanding some concepts of data structures in Python, in the following code.
class Stack(object): #1
def __init__(self): #2
self.items=[]
def isEmpty(self):
return self.items ==[]
def push(self,item):
self.items.append(item)
def pop(self):
self.items.pop()
def peak(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
s = Stack()
s.push(3)
s.push(7)
print(s.peak())
print (s.size())
s.pop()
print (s.size())
print (s.isEmpty())
I don't understand what is this object argument
I replaced it with (obj) and it generated an error, why?
I tried to remove it and it worked perfectly, why?
Why do I have __init__ to set a constructor?
self is an argument, but how does it get passed? and which object does it represent, the class it self?
Thanks.
object is a class, from which class Stack inherits. There is no
class obj, hence error. However, you can define a class that does
not inherit from anything (at least, in Python 2).
self represents an object on which the method is called; for
example when you do s.pop(), self inside method pop refers to
the same object as s - it is not a class, it is an instance of the class.
1
object here is the class your new class inherits from. There is already a base class named object, but there is no class named obj which is why replacing object with obj would cause an error. Anyway in your example code it is not needed at all since all classes in python 3 implicitly extends the object class.
2
__init__ is the constructor of the object and self there represents the object that you are creating itself, not the class, just like in the other methods you made.
Point 1:
Some history required here... Originally Python had two distinct kind of types, those implemented in C (whether in the stdlib or C extensions) and those implemented in Python with the class statement. Python 2.2 introduced a new object model (known as "new-style classes") to unify both, but kept the "classic" (aka "old-style") model for compatibility. This new model also introduced quite a lot of goodies like support for computed attributes, cooperative super calls via the super() object, metaclasses etc, all of which coming from the builtin object base class.
So in Python 2.2.x to 2.7.x, you can either create a new-style class by inheriting from object (or any subclass of object) or an old-style one by not inheriting from object (nor - obviously - any subclass of object).
In Python 2.7., since your example Stack class does not use any feature of the new object model, it works as well as an 'old-style' or as a 'new-style' class, but try to add a custom metaclass or a computed attribute and it will break in one way or another.
Python 3 totally removed old-style classes support and object is the defaut base class if you dont explicitely specify one, so whatever you do your class WILL inherit from object and will work as well with or without explicit parent class.
You can read this for more details.
Point 2.1 - I'm not sure I understand the question actually, but anyway:
In Python, objects are not fixed C-struct-like structures with a fixed set of attributes, but dict-like mappings (well there are exceptions but let's ignore them for the moment). The set of attributes of an object is composed of the class attributes (methods mainly but really any name defined at the class level) that are shared between all instances of the class, and instance attributes (belonging to a single instance) which are stored in the instance's __dict__. This imply that you dont define the instance attributes set at the class level (like in Java or C++ etc), but set them on the instance itself.
The __init__ method is there so you can make sure each instance is initialised with the desired set of attributes. It's kind of an equivalent of a Java constructor, but instead of being only used to pass arguments at instanciation, it's also responsible for defining the set of instance attributes for your class (which you would, in Java, define at the class level).
Point 2.2 : self is the current instance of the class (the instance on which the method is called), so if s is an instance of your Stack class, s.push(42) is equivalent to Stack.push(s, 42).
Note that the argument doesn't have to be called self (which is only a convention, albeit a very strong one), the important part is that it's the first argument.
How s get passed as self when calling s.push(42) is a bit intricate at first but an interesting example of how to use a small feature set to build a larger one. You can find a detailed explanation of the whole mechanism here, so I wont bother reposting it here.

Categories