Instances of class A as class attributes of A in Python? - python

Are there Python versions that allow defining your class like this:
class Foo:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
and then adding class attributes, such as BAR_1, BAR_2, etc.:
class Foo:
BAR_1 = ...
BAR_2 = ...
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
which are actually "special cases of Foo", such as:
class Foo:
BAR_1 = Foo(4, 9, 16)
BAR_2 = Foo(2, 3, 5)
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
so that, in my code, I can either make my own Foos or get common, predefined Foos by working directly with Foo.BAR_1 and Foo.BAR_2?
The code above obviously does not work, otherwise I would not post the question (Foo is an unresolved reference when defining BAR_1 and BAR_2). I found a trick on SO how to sort-of achieve this -> defining a custom ClassProperty class:
class ClassProperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
which then allows me to define Foo as
class Foo:
#ClassProperty
def BAR_1(cls):
return Foo(4, 9, 16)
#ClassProperty
def BAR_2(cls):
return Foo(2, 3, 5)
...
and that works, but the issue is that Foo.__init__ is called everytime whenever Foo.BAR_1 or Foo.BAR_2 is retrieved, which can be useful in certain situations (precisely those where you always want separate instances), but in the special case where Foo is simply a messenger class which is coincidentally hard to load (like a result of a computation for example), this solution is unfeasible. I'd like for the constructor of Foo to be called exactly once for BAR_1, exactly once for BAR_2 (ideally lazily, during the first retrieval, that would be fantastic), and after that it would only return the created instances. So, is there a way to do this?
I use Python 3.8.6.

During the time I composed the question body, I figured out I can just define Foo like this:
class Foo:
#ClassProperty
def BAR_1(cls):
if not hasattr(cls, '_Foo__BAR_1'):
cls.__BAR_1 = Foo(4, 9, 16)
return cls.__BAR_1
#ClassProperty
def BAR_2(cls):
if not hasattr(cls, '_Foo__BAR_2'):
cls.__BAR_2 = Foo(2, 3, 5)
return cls.__BAR_2
Now, I can call Foo.BAR_X for retrieval of a defined Foo.__BAR_X which is actually an instance of Foo, which is always created only once.

Related

Upcast instance to parent class in python

(How) Is it possible in Python to treat an instance of class B exactly as an instance of class A, where A is a parent of B (like up-casting in compiled languages)?
Say we have the following:
class A:
def __init__(self, prop=None):
self.prop = prop
def f(self):
return 'A.f'
def g(self):
return 'A.g', self.f()
class B(A):
def f(self):
return 'B.f'
def g(self):
return 'B.g', self.f()
Calling A().g() produces ('A.g', 'A.f'), whereas B().g() produces ('B.g', 'B.f'). Calling super(B, B()).g() produces ('A.g', 'B.f'), which is different from compiled languages, so I cannot use super. Instead, I need a function that changes the type from which an instance's methods are resolved, but preserves (a reference to) the original state. In short, I'm looking for a way to do this:
b = B(object())
a = upcast(b, A)
a.prop = object()
assert isinstance(a, A)
assert a.g() == ('A.g', 'A.f')
assert a.prop is b.prop
The closest I could get is
a = copy.copy(b)
a.__class__ = A
a.__dict__ = b.__dict__
(assuming A/B are "nice" "heap" classes), but this makes unnecessary copies of all objects in the __dict__ before I discard them. Is there a better way to do this?

Access all variables by their names of specific object in class

I have two python classes:
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
Now I have an instance of B and need to access param1, I can just write b.a.param1. But the goal is to omit the 'a' part, so access this param with b.param1. I could add property to class B, but I am searching for generic solution - when A class has a lot variables. Is it possible? And would it be clean solution?
This is not the most elegant option and probably a bad practice but you can copy all the attributes of a to be attributes of b using getattr and setattr:
import inspect
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
variables = [i for i in dir(a) if not inspect.ismethod(i) and i[:2] != '__']
for var in variables:
setattr(self, var, getattr(a, var))
This way you can access a's attributes directly:
a = A(1)
b = B(a)
b.param1
which will return
1

Global vars in python [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I currently have some class foo() with some variables that are not only shared among all instances of the foo class, but also by other classes bar.
i.e.
class foo():
__init__(self, a, b):
self.a = a
self.b = b
class bar():
__init__(self, a, b):
self.a = a
self.b = b
One solution would be to make a and b class variables, but how do I do that cleanly during construction? Could I just put both classes in the same file and have them reference some global variables a and b? Is that bad practice?
Since you did not provide your intention or real-world situation, I'll just provide some ways of sharing variable access.
1st option: global.
a=b=None
class foo():
def __init__(self, _a, _b):
global a, b
a, b = _a, _b
class bar():
def __init__(self, _a, _b):
global a, b
a, b = _a, _b
2nd option: foo's class vars
class foo():
a = b = None
def __init__(self, a, b):
foo.a, foo.b = a, b
class bar():
def __init__(self, a, b):
foo.a, foo.b = a, b
3rd option: inheritance
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar(foo):
pass
4th option: outer class
class outer():
a = b = None
class foo():
def __init__(self, a, b):
outer.a, outer.b = a, b
class bar():
def __init__(self, a, b):
outer.a, outer.b = a, b
5th option: compsition
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar():
def __init__(self, a, b):
self.foo = foo(a,b)
6th option: closure over outer-function local variables
def outer():
a = b = None
class foo():
def __init__(self, _a, _b):
nonlocal a, b
a, b = _a, _b
class bar():
def __init__(self, _a, _b):
nonlocal a, b
a, b = _a, _b
... #things with foo and bar
7th option: closure over foo's __init__ local variables.
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar():
nonlocal a, b
#do things with a and b directly
self.bar = bar()
You could do this:
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
class Bar(Foo):
pass
By inheriting from Foo, you'll be adopting Foo's construction method as well so it will act the same way. If you need to override it, you can set it up this way in Bar:
def __init__(self, a, b, c):
super(Bar, self).__init__(a, b)
self.c = c
super will call your base class' method first (in this case, Foo) and then allow you to add on if you'd like. Here's the documentation on super, if you're interested.
The usual solution is to make an object that stores the shared information, then pass that when instantiating the classes that need it. Often this is some kind of configuration information, so we'll call the class Config:
class Config(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# default values
number = 0
text = "Nothing"
Since Python is duck-typed, any object can be used to hold this configuration; it can be an instance of a class or the class itself. The former is handy when the data is specified at runtime. The latter can be convenient since it allows the programmer to define the various bundles of attributes using inheritance at coding time. The Config class here lets you have it either way: you can instantiate it, passing keyword arguments with the shared values, or you can subclass it, providing the shared values as class attributes.
In your Foo and Bar classes you then just accept the shared data in the constructor:
# these classes both need certain pieces of data
# but are not related by inheritance
class Foo(object):
def __init__(self, shared):
self.shared = shared
class Bar(object):
def __init__(self, config):
self.config = config
And then you can either instantiate the Config class, or define a subclass, and pass the resulting object to the new objects:
# use an instance
adams_config = Config(text="Don't Panic", number=42)
foo1, bar1 = Foo(adams_config), Bar(adams_config)
# use a subclass
class LincolnConfig(Config):
number = 87
text = "Four score and seven years ago"
foo2, bar2 = Foo(LincolnConfig), Bar(LincolnConfig)
Now methods of your Foo and Bar class can get self.shared.number or self.config.text (and so on) to access the data.
Since the instances of your various classes are all holding references to the same object, a change to e.g. adams_config or LincolnConfig would be seen by any instance of any class that holds a reference to one of these objects. If this isn't the behavior you want, you could fish the data you want to "freeze" out of the config object at instantiation and set it as attributes of your instance.
You could also just use a dictionary for data you want to access in various places, but I think the benefits of inheritance and attribute-access syntax are a good argument for doing it with classes.
You could even have a global configuration object that is used as a default value so you don't need to explicitly specify it if you want things to "just work." Here we'll just use the Config class itself for that, since it already has default values for the attributes we're interested in:
class Baz(object):
def __init__(self, config=Config):
self.config = config
By using this approach instead of global variables, you make it easier for clients using your objects to have numerous instances with different settings, rather than being limited to one "bundle" of settings for all instances.
I'm not sure what you mean by "cleanly during construction"
You can use class variables by defining them outside the function like:
class A:
x = 1
def __init__(self):
pass
And just call A.x whenever you need the variable, within other classes or wherever

Python object conversion

Assume that we have an object k of type class A. We defined a second class B(A). What is the best practice to "convert" object k to class B and preserve all data in k?
This does the "class conversion" but it is subject to collateral damage. Creating another object and replacing its __dict__ as BrainCore posted would be safer - but this code does what you asked, with no new object being created.
class A(object):
pass
class B(A):
def __add__(self, other):
return self.value + other
a = A()
a.value = 5
a.__class__ = B
print a + 10
a = A() # parent class
b = B() # subclass
b.value = 3 # random setting of values
a.__dict__ = b.__dict__ # give object a b's values
# now proceed to use object a
Would this satisfy your use case? Note: Only the instance variables of b will be accessible from object a, not class B's class variables. Also, modifying variables in a will modify the variable in b, unless you do a deepcopy:
import copy
a.__dict__ = copy.deepcopy(b.__dict__)
class A:
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, parent_instance, c):
# initiate the parent class with all the arguments coming from
# parent class __dict__
super().__init__(*tuple(parent_instance.__dict__.values()))
self.c = c
a_instance = A(1, 2)
b_instance = B(a_instance, 7)
print(b_instance.a + b_instance.b + b_instance.c)
>> 10
Or you could have a sperate function for this:
def class_converter(convert_to, parent_instance):
return convert_to(*tuple(parent_instance.__dict__.values()))
class B(A):
def __init__(self, *args):
super().__init__(*args)
self.c = 5
But using the 2nd method, I wasn't able to figure out how to pass additional values

Avoid specifying all arguments in a subclass

I have a class:
class A(object):
def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
#do some init stuff
And I have a subclass which needs one extra arg (the last W)
class B(A):
def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
self.__W=W
It seems dumb to write all this boiler-plate code, e.g passing all the args from B's Ctor to the inside call to A's ctor, since then every change to A's ctor must be applied to two other places in B's code.
I am guessing python has some idiom to handle such cases which I am unaware of. Can you point me in the right direction?
My best hunch, is to have a sort of Copy-Ctor for A and then change B's code into
class B(A):
def __init__(self,instanceOfA,W):
A.__copy_ctor__(self,instanceOfA)
self.__W=W
This would suit my needs since I always create the subclass when given an instance of the father class, Though I am not sure whether it's possible...
Considering that arguments could be passed either by name or by position, I'd code:
class B(A):
def __init__(self, *a, **k):
if 'W' in k:
w = k.pop('W')
else:
w = a.pop()
A.__init__(self, *a, **k)
self._W = w
Edit: based on Matt's suggestion, and to address gnibbler's concern re a positional-argument approach; you might check to make sure that the additional subclass-specific argument is being specified—similar to Alex's answer:
class B(A):
def __init__(self, *args, **kwargs):
try:
self._w = kwargs.pop('w')
except KeyError:
pass
super(B,self).__init__(*args, **kwargs)
>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3
Original answer:
Same idea as Matt's answer, using super() instead.
Use super() to call superclass's __init__() method, then continue initialising the subclass:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, w, *args):
super(B,self).__init__(*args)
self.w = w
In situations where some or all of the arguments passed to __init__ have default values, it can be useful to avoid repeating the __init__ method signature in subclasses.
In these cases, __init__ can pass any extra arguments to another method, which subclasses can override:
class A(object):
def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
self.a = a
self.b = b
# …
self._init_extra(*args, **kwargs)
def _init_extra(self):
"""
Subclasses can override this method to support extra
__init__ arguments.
"""
pass
class B(A):
def _init_extra(self, w):
self.w = w
Are you wanting something like this?
class A(object):
def __init__(self, a, b, c, d, e, f, g):
# do stuff
print a, d, g
class B(A):
def __init__(self, *args):
args = list(args)
self.__W = args.pop()
A.__init__(self, *args)

Categories