I would like to choose a subclass at instantiation to select specific attributes from certain classes. I've worked out the following:
Code
class Foo:
a = "foo"
def __init__(self, dtype=Bar):
self.__class__ = dtype
class Bar:
b = "bar"
class Baz(Bar):
c = "baz"
Demo
Foo(dtype=Bar).b
# 'bar'
Foo(dtype=Baz).b
# 'bar'
Foo(dtype=Baz).c
# 'baz'
This gives the desired result, selecting specific attributes from Bar while optionally extending features with Baz. Unlike subclassing however, we have no access to Foo's attributes.
Foo(dtype=Baz).a
# AttributeError: 'Baz' object has no attribute 'a'
There are occasions when not all attributes are desired, so subclassing Foo(Baz) is not preferred.
What's the idiomatic analog to accomplish this in Python?
Why create an instance of Foo in the first place if you really want an instance of Bar or Baz? Instead, make Foo a factory for instance of Bar and Baz.
class Bar:
b = "bar"
class Baz(Bar):
c = "baz"
class Foo:
a = "foo"
def __new__(cls, dtype=Bar):
return dtype()
As I mentioned in the comments, just use plain inheritance.
class Foo:
a = "foo"
class Bar(Foo):
b = "bar"
class Baz(Foo):
c = "baz"
# Example use
import random
class_to_instantiate = Bar if random.choice([True, False]) else Baz
print(class_to_instantiate().a)
Outputs
foo
Related
Let's say I have a class defined as follow :
class Foo():
baz = None
def __init__(self, bar):
self.bar = bar
Now, in that example Foo.baz is None. Now let's say that this class attribute needs to be an instance of Foo like below:
class Foo():
baz = Foo("baz")
def __init__(self, bar):
self.bar = bar
How would I proceed?
Similarly, is there any way to create a "class property". I know I can assign a lambda function returning a new instance of the class to a class attribute, but I'd rather not have to write the parenthesis.
If you want to use the line baz = Foo("baz") inside a class even before defining Foo earlier; it's not possible ie Python will throw a NameError: name 'Foo' is not defined at you, but here's a workaround to achieve what you intend:
class Foo:
def __init__(self, bar):
self.bar = bar
Foo.baz=Foo('foo')
Now Foo.baz is an instance of the Foo class and also baz is a class attribute of the Foo class and hence will be inherited by all instances of the class as well:
myFoo = Foo('myFoo')
You can verify that myFoo.baz.bar is infact 'foo' and not 'myFoo'
print(myFoo.bar)
# myFoo
print(myFoo.baz.bar)
# foo
Also note that this will cause Foo.baz, Foo.baz.baz, Foo.baz.baz.baz etc. all to be a Foo object because Foo.baz has value Foo('foo') ie an object of Foo class and baz is also a class attribute of Foo.
This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
I want to make properties from an attribute of my class accessible directly through the instance of the class (without inheriting from it). So basically if I have:
class A:
#property
def foo(self):
print("foo")
#property
def bar(self):
print("bar")
class B:
def __init__(self):
self._a = A()
Instead of doing b._a.bar I want to be able to do b.bar. Based on this answer here, I tried the following in class B:
class B:
def __init__(self):
self._a = A()
attrs = [attr for attr in dir(self._a)
if not callable(self._a.__getattribute__(attr))
and not attr.startswith("__")]
for attr in attrs:
setattr(self.__class__, attr,
property(lambda s: s._a.__getattribute__(attr)))
But when instantiating and testing it out, I get one of those weird python moments:
>>> b = B()
foo
bar
>>> b.foo
bar
>>> b.bar
bar
Why are both 'foo' and 'bar' printed out when creating the instance ?
How does the 'foo' property point to the same getter as 'bar' ?
bar and foo are printed when you create the instance because doing _a.__getattribute__("foo") and _a.foo will both call the property object to get a value.
Both of the attributes you set up in B use lambdas to get the correct property from A. This is a common mistake when calling lambdas. Because the attr value is inherited from the outside scope, it isn't frozen when the lambda is evaluated. Instead, it is simply the same attr reference as the enclosing scope's attr, and changes accordingly. So all of your lambdas will have the same attr value.
You can define a B.__getattr__ method instead. This method is called when ordinary attribute lookup fails.
class B:
def __init__(self):
self._a = A()
def __getattr__(self, name):
return getattr(self._a, name)
b = B()
b.bar # bar
b.foo # foo
I have one class (Bar) embedded inside another class (Foo).
class Foo():
class Bar():
def __init__(self):
self.a = 1
self.b = 2
...
self.z = 26
def __init__(self):
self.bar = Bar()
To access the attributes of class Bar, the user would need to the following:
>>> f = Foo()
>>> f.bar.a
1
How can I setup a short dot notation so that users can use BOTH:
>>> f.bar.a
1
and
>>> f.a
1
In my example, I'm trying to demonstrate that Bar class has a lot of variables. So I don't want to write a getter/setter for each one manually. So I was thinking to use the property() in a for loop like this:
def __init__(self):
self.bar = Bar()
# Allow shorter dot notation
for parm in self.bar.__dict__:
setattr(self, i, getattr(bar, i))
self.i = property(...)
But I'm unsure how to use property in this context without manually writing several setter functions.
Any suggestions on how to allow access to both shorter and longer notations?
That's what the __getattr__hook is ideally suited for:
class Foo:
# ...
def __getattr__(self, name):
return getattr(self.bar, name)
__getattr__ is only called for attributes that are missing; so only attributes that are not already present on instances of Foo() are passed to Foo().__getattr__(). The getattr() function then lets you use the same attribute name on self.bar; if the attribute doesn't exist there either, an AttributeError is thrown, as would be expected.
This may be a stupid / trivial question, but I'm confused in this matter.
What is the encouraged (pythonic) way of declaring instance fields - in the constructor, or in the class body itself?
class Foo:
""" Foo class """
# While we are at it, how to properly document the fields?
bar = None
def __init__(self, baz):
""" Make a Foo """
self.bar = baz
OR:
class Foo:
""" Foo class """
def __init__(self, baz):
""" Make a Foo """
self.bar = baz
It's a matter of expectations. People reading your code will expect that the attributes defined at the top level of the class will be class attributes. The fact that you then always replace them in __init__ will only create confusion.
For that reason you should go with option 2, defining instance attributes inside __init__.
In terms of documenting the attributes, pick a docstring style and stick to it; I like Google's, other options include numpy's.
class Foo:
"""A class for foo-ing bazs.
Args:
baz: the baz to foo
Attributes:
bar: we keep the baz around
"""
def __init__(self, baz):
self.bar = baz
To keep it simple, let us define class Foo with a class variable bar:
In [34]: class Foo: bar = 1
Now, observe:
In [35]: a = Foo()
In [36]: a.bar
Out[36]: 1
In [37]: Foo.bar = 2
In [38]: a.bar
Out[38]: 2
A change to Foo.bar affects existing instances of the class.
For this reason, one generally avoids class variables, as opposed to instance variables unless one wants these side-effects.
class ClassName(object):
"""
"""
def __init__(self, foo, bar):
"""
"""
self.foo = foo # read-write property
self.bar = bar # simple attribute
def _set_foo(self, value):
self._foo = value
def _get_foo(self):
return self._foo
foo = property(_get_foo, _set_foo)
a = ClassName(1,2)
#a._set_foo(3)
print a._get_foo()
When I print a._get_foo() the function _get_foo prints the variable self._foo .
But where does it come from?
self._foo and self.foo are different, aren't they?
EDIT: The problem is that I still not understand what property does. Give me some time.
Docs for property explain how it's supposed to be used. There is no difference between these lines:
self.foo = foo # read-write property
and
a.foo = 3
You're also not supposed to call setters and getters manually.
eta: if you don't understand what property does after looking at the examples provided in the docs and reading our answers, you perhaps should just abstain from using it. It really is not the most essential of Python's features. You might want to start with a simpler example, which is easy to understand:
>>> class ClassName(object):
def __init__(self, foo):
self.foo = foo
>>> a = ClassName(1, 2)
>>> a.foo
1
>>> a.foo = 42
>>> a.foo
42
I really recommend this site:
http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/
A good explanation of the property-function.
You define foo to be a property with getters and setters.
Those getters and setters use the variable self._foo, since you coded it that way:
def _set_foo(self, value):
self._foo = value
In your case you would get a problem if you had a slightly different implementation:
class ClassName(object):
"""
"""
def __init__(self,):
"""
"""
pass
def _set_foo(self, value):
self._foo = value
def _get_foo(self):
return self._foo
foo = property(_get_foo, _set_foo)
a = ClassName()
print a.foo
-> AttributeError
b = ClassName()
b.foo = 1 # implicitely sets self._foo !
print b.foo
>> OK, this is 1
As it happens you indeed should set self.foo in the __init__ since the setter creates self._foo. (Or initialize self._foo in the __init__ directly of course).
So, where do self.foo and self._foo come from? It's explicit in the code. self.foo is a property, and self._foo is the variable in which you decided to keep the value of that property.
self._foo and self.foo would be different except that you have assigned the property you created to the name foo: foo = property(_get_foo, _set_foo).
So now you have two names defined foo which is property that is defined in terms of the methods _set_foo and _get_foo. You also have a name _foo defined but this is the same attribute used in _set_foo and _get_foo and therefore in property foo.
So you two names but only one object.
foo
/\
/ \
_set_foo _get_foo
\ /
\ /
_foo --> object
Note: For the example you have Python this is overkill, there is no need to use getter and setter methods unless they are going to have business logic in them. Just use an attribute named foo, you can always wrap that in a property in future without affecting client code if it turns out that you need some smarts around the access of the attribute.
Change your code to:
class ClassName(object):
def __init__(self, foo, bar):
self.foo = foo # read-write property
self.bar = bar # simple attribute
Both foo and bar are read write, if you need finer control you can then consider using a property.
According to your code, it "comes from" _set_foo. In your init, when you do self.foo = foo, that calls _set_foo(1), which performs self._foo = 1.
You can see this more clearly if you add a print statement inside _set_foo().