Check if instance of python class has attribute without instantiating the class - python

Is it possible to check if an instance of a class has an attribute without (being able to) instantiating the class?
Example:
class A where I lack knowledge about arguments to instantiate:
class A:
def __init__(self, unkown):
self.a = 1
checking for attribute a in class A fails:
hasattr(A,'a')
>> False
checking for attribute a in instance of class A would succeed:
hasattr(A(1),'a')
>> True
Is it possible to check for attribute a without instantiating A? Maybe with something different than hasattr?

In general this is not possible. You can freely set any attribute on any instance of A at any time. Consider
a = A('foo')
setattr(a, input('attr_name: '), 'bar')
A cannot know what attributes may or may not be set on its instances in the future.
You could do some insane things like parse __init__ if you want to limit the question to attributes set in __init__, but most likely we're having an XY-problem here.

As it's not possible, a workaround can be making a a property:
class A:
def __init__(self, unkown):
self._a = 1
#property
def a(self):
return self._a
hasattr(A,'a')
>> True
Just leaving this here in the sense of completeness

Related

correct way of extending a class in python

I am given a designated factory of A-type objects. I would like to make a new version of A-type objects that also have the methods in a Mixin class. For reasons that are too long to explain here, I can't use class A(Mixin), I have to use the A_factory. Below I try to give a bare bones example.
I thought naively that it would be sufficient to inherit from Mixin to endow A-type objects with the mixin methods, but the attempts below don't work:
class A: pass
class A_factory:
def __new__(self):
return A()
class Mixin:
def method(self):
print('aha!')
class A_v2(Mixin): # attempt 1
def __new__(cls):
return A_factory()
class A_v3(Mixin): # attempt 2
def __new__(cls):
self = A_factory()
super().__init__(self)
return self
In fact A_v2().method() and A_v3().method() raises AttributeError: 'A' object has no attribute 'method'.
What is the correct way of using A_factory within class A_vn(Mixin) so that A-type objects created by the factory inherit the mixin methods?
There's no obvious reason why you should need __new__ for what you're showing here. There's a nice discussion here on the subject: Why is __init__() always called after __new__()?
If you try the below it should work:
class Mixin:
def method(self):
print('aha!')
class A(Mixin):
def __init__(self):
super().__init__()
test = A()
test.method()
If you need to use a factory method, it should be a function rather than a class. There's a very good discussion of how to use factory methods here: https://realpython.com/factory-method-python/

Use class instance as parent class in python

I'm trying to get a better understanding of python class system. This question is intended only to satisfy my curiosity.
Is it possible to use somehow a class instance as a parent class for another class. So far what I tried to do is
class A:
pass
a = A()
class B(a):
pass
and it gives following error: TypeError: object() takes no parameters
class Meta(type):
pass
class A:
pass
a = A()
class B(a, metaclass=Meta):
pass
and it gives this error TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class
Is it possible to use somehow a class instance as a parent class for another class
Well, actually that's always the case since classes ARE instances (of their metaclass). But then the class of the instance you want to use as a parent class (yeah, re-read it thrice...) must behave as if it was a genuine metaclass - which can rapidily become impractical.
I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class
There might be a way indeed (overriding __getattr__() or __getattribute__()), but devil is in the details and chances are that by the time you make this rube goldberg contraption "kind-of-work-with-severe-limitations-and-corner-cases", you will wonder if that was really worth the pain.
Note that OTHO both composition/delegation and dynamic (runtime) creation / modification of classes is absurdly easy in Python so I can hardly think of a problem that would be better solved by (ab)using an instance as a parent class.
Every instance has a __class__ attribute that refers to the underlying class structure.
Knowing this, you can actually do the following:
class A(object):
def foo(self):
return 'hello'
a = A()
class B(a.__class__):
pass
b = B()
print(b.foo()) # Prints 'hello'
Alternatively, you can also use type:
class B(type(a)):
pass
b = B()
print(b.foo()) # Prints 'hello'
Your instance needs to be a type in order to be used in inheritance chains. For example:
class A(type):
pass
a = A(str) # you need to pass the type's `type` to the construcor
class B(a):
pass
That being said, there's little, if no practical application for this that I can think of - or rather whatever you achieve this way will be easier and more Pythonic to achieve through normal inheritance and metaclasses.
EDIT:
OK, so it is possible to hack if you have control over the base class (either using metaclasses or __init_subclass__ (python 3.6 or later))
class A:
x = 1
def __init_subclass__(cls, inst, **kwargs):
super().__init_subclass__(**kwargs)
for k, v in vars(inst).items():
if k not in dir(cls): # i.e. don't override but remove if you want to
setattr(cls, k, v)
a = A()
a.y = 2
class B(A, inst=a):
pass
B.x # --> 1
B.y # --> 2
You're sort of on the right tracks with your second example. Yes it is possible.
class Meta(type): pass
class A(metaclass=Meta): pass
class B(A): pass
issubclass(B, A) # --> True
isinstance(B, Meta) # --> True
Thats because A is an instance of Meta (thats what a metaclass means, a class whose instance is also a class). Hence B is a subclass of an instance of Meta.
So yes, you could set for example
class Meta(type):
def __init__(cls, *args, **kwargs):
cls.x = 1
super().__init__(cls, *args, **kwargs)
class A(Meta):
def __init__(self):
self.y = 2
A.x # 1 (acts as an instance of Meta)
a = A()
a.y # 2 (acts as an instance of A)
a.x # AttributeError (does not act as an instance of Meta)

Use abstract base class to force implementation of class property?

I would like to use an abstract base class to force implementation of a class attribute in a concrete class. I know how to force implementation of a generic attribute using #abc.abstractproperty. There are lots of SO answers about how to do that - I've read about 10 of them :) But I would like to ensure that the concrete class must define the abstract attribute as a class attribute and NOT as an instance attribute. Anyone know how to do this?
EDITED to address question:
I have users who will define concrete classes from the ABC. Certain abstract properties need to be "concretized" as class attributes. The checking needs to happen the first time they instantiate the concrete class - not sooner. Ideally, if they mistakenly define an abstract property as an instance attribute, a TypeError will be raised that flags their mistake.
The point is that the value of the class attribute should be the same for all instances of the concrete class.
I think I am missing some knowledge about Python internals that would help me address this question properly...
import abc
class MyABC(object):
__metaclass__ = abc.ABCMeta
#abc.abstractproperty
def foo():
return 'we never run this line'
# I want to enforce this kind of subclassing
class GoodConcrete(MyABC):
#classmethod
def foo(cls):
return 1 # value is the same for all class instances
# I want to forbid this kind of subclassing
class BadConcrete(MyABC):
def foo(self, val):
self.foo = val

python: changing object class in multi-level inheritance

I have looked at many questions posted here to find an answer to my problem, but I wasn't successful. The problem might be, that I just don't know for what keywords I should look. So my problem is the following:
I've got a program, that has a multi-level inheritance and I am trying to figure out how the best way would be to change the class of an object to a subclass. Let's say I have the following code:
class A(object):
def __init(self, filename, ..)
super(A, self).__init__()
...some assignments here
class B(A):
def __init(self, filename, ..)
super(B, self).__init__()
...some assignments here
class C(A):
def __init(self, filename, ..)
super(C, self).__init__()
...some assignments here
and so on...
I always want to start initialising an object of class A. Depending on the type of the file that is used, the assignments may differ and depending on those assignments I can determine what kind of file it is. So now I want to change the class of the object to whatever class is suitable..
I know I could pass the A object to B or C and use copy or deepcopy, but in A i am assigning an object of which the reference should not change and some others where it should change. Also I would need to delete that object of A, after initialising B or C.
class B(A):
def __init__(self, filename, objA = None):
if objA is not None:
self.__dict__ = copy.deepcopy(objA.__dict__)
del(objA)
else:
super(B, self).__init__(filename)
Also there is another possibility by changing the _class attribute to another class and use some kind of update method of the new class.
I would like to know, which of the two approaches is recommended or is there even a better one. Thanks in advance.
What you want is a factory: a function that opens the file, reads the stuff it needs to read to figure out what kind of file it is, and then initializes and returns an object of the appropriate class.
If you want to keep it a class, you'd want to override __new__() and then return an object of the desired class instead of its own class. (You could also do it using a metaclass and overriding __call__() on that.)
You can change an instance's class after instantiating it as well, by changing its __class__ attribute to point to the desired class. That'll work, but the factory is going to be more familiar to other programmers who will read your code.
This code will explain what you want to do
class B(object):
def x(self):
print 'b'
class A(object):
def x(self):
print 'a'
Now we create two objects
a = a()
b = b()
a.x()
a
b.x()
b
now if you want 'a' to become a B object
a.__class__ = type(b)
or
a.__class__ = B
now the x attribute is from the B class.
a.x()
b

Does Python have class prototypes (or forward declarations)?

I have a series of Python classes in a file. Some classes reference others.
My code is something like this:
class A():
pass
class B():
c = C()
class C():
pass
Trying to run that, I get NameError: name 'C' is not defined. Fair enough, but is there any way to make it work, or do I have to manually re-order my classes to accommodate? In C++, I can create a class prototype. Does Python have an equivalent?
(I'm actually playing with Django models, but I tried not complicate matters).
Actually, all of the above are great observations about Python, but none of them will solve your problem.
Django needs to introspect stuff.
The right way to do what you want is the following:
class Car(models.Model):
manufacturer = models.ForeignKey('Manufacturer')
# ...
class Manufacturer(models.Model):
# ...
Note the use of the class name as a string rather than the literal class reference. Django offers this alternative to deal with exactly the problem that Python doesn't provide forward declarations.
This question reminds me of the classic support question that you should always ask any customer with an issue: "What are you really trying to do?"
In Python you don't create a prototype per se, but you do need to understand the difference between "class attributes" and instance-level attributes. In the example you've shown above, you are declaring a class attribute on class B, not an instance-level attribute.
This is what you are looking for:
class B():
def __init__(self):
self.c = C()
This would solve your problem as presented (but I think you are really looking for an instance attribute as jholloway7 responded):
class A:
pass
class B:
pass
class C:
pass
B.c = C()
Python doesn't have prototypes or Ruby-style open classes. But if you really need them, you can write a metaclass that overloads new so that it does a lookup in the current namespace to see if the class already exists, and if it does returns the existing type object rather than creating a new one. I did something like this on a ORM I write a while back and it's worked very well.
A decade after the question is asked, I have encountered the same problem. While people suggest that the referencing should be done inside the init method, there are times when you need to access the data as a "class attribute" before the class is actually instantiated. For that reason, I have come up with a simple solution using a descriptor.
class A():
pass
class B():
class D(object):
def __init__(self):
self.c = None
def __get__(self, instance, owner):
if not self.c:
self.c = C()
return self.c
c = D()
class C():
pass
>>> B.c
>>> <__main__.C object at 0x10cc385f8>
All correct answers about class vs instance attributes. However, the reason you have an error is just the order of defining your classes. Of course class C has not yet been defined (as class-level code is executed immediately on import):
class A():
pass
class C():
pass
class B():
c = C()
Will work.

Categories