Method namespaces/collections in Python classes - python

Say I have a class Foo and I do foo = Foo(). I want some kind of "method namespace" foo.bar that is not shared across Foo instances, to which I dynamically can add methods that operate on foo:
def method(self, someValue):
self.value = someValue + 10
foo.bar.m = method
foo.bar.m(20)
And then I want to find that foo.value is 30.
Any way to accomplish this?

Is this what you're looking for?
import types
class Namespace(object):
pass
class Foo(object):
def __init__(self, value):
self.value = value
self.bar = Namespace()
def function(self, new_value):
self.value = new_value
a = Foo(1)
b = Foo(2)
b.bar.function = types.MethodType(function, b, Foo)
b.bar.function(6)
print a.value # prints 1
print b.value # prints 6
The trick is using the types module to convert the test function into a method that can be bound to an instance of the object.
When I run this line: b.bar.function = types.MethodType(function, b, Foo), I am essentially telling Python to create a new method that binds function to the b instance of Foo. I can then take this method and store it inside any arbitrary location.
Since the method is permanently bound to the b instance of Foo, self will always refer to b regardless of which object the method is actually assigned to.

Related

Can a python class method have as arguments the same class variable updated in init(self)?

I have two python classes
class A:
"""
This is a class retaining some constants
"""
C=1
class B:
VAR = None
def __init__(self):
b.VAR = A
def f(self, v=VAR ):
print(v.C)
clb = B()
clb .f()
AttributeError: 'NoneType' object has no attribute 'C'
So what I am trying to do is populate the B::VAR class variable in the B::init() with the reference of class A, and after that in the B::f() to have access to A::C by using default argument v (that retains VAR).
I intend to use v as a default value for the code inside B::f() and if needed to change it when calling the function.
Is my scenario possible?
Thank you,
Yes, this is possible:
class A:
"""
This is a class retaining some constants
"""
C = 1
class B:
VAR = None
def __init__(self):
self.VAR = A
def f(self, v=None):
if v is None:
v = self.VAR
print(v.C)
clb = B()
clb.f()
You issue is that the default arguments v=VAR is an old reference to the B.VAR which is None, not the updated value of the object clb.VAR.
This diagram show that the old version of f() have a default value for v that point to None, because this is computed at the definition of the method, when the class B is defined, before any creation of clb: B object, where VAR is a class attribute.
My suggestion is to set v at runtime using the VAR of the object throught self, which is changed in the __init__ to A.
class A:
C = 1
class B:
VAR = None
def __init__(self):
B.VAR = A
#classmethod
def f(cls):
print(cls.VAR.C)
clb = B()
clb.f()
This is another way to do it. However, I'm wondering what it is you're actually trying to do, because this seems really strange

After using property decorator, python object has two very similar attributes (foo.bar and foo._bar). Is that ok?

So I'm refactoring my code to be more Pythonic - specifically I've learned that using explicit getters and setters should be replaced with #property. My case is that i have an Example class with initialized bar attribute (initialization helps me to know that user set the bar):
class Example:
def __init__(self):
self.bar = 'initializedValue'
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, b):
self._bar = b
def doIfBarWasSet():
if self.bar != 'initializedValue':
pass
else:
pass
after running foo = Example() my debugger shows that foo has two attributes: _bar and bar, both set to 'initializedValue'. Also, when I run foo.bar = 'changedValue' or foo._bar = 'changedValue', both of them are changed to 'changedValue'. Why there are two attributes? Isn't that redundant? I think I understand why there is _bar attribute - I added it in #bar.setter, but why there is bar as an string attribute? Shouldn't bar be rather a method leading to bar #property?
It's fine. Keep in mind that bar is not an instance attribute, but a class attribute. Since it has type property, it implements the descriptor protocol so that its behavior is different when accessed from an instance. If e is an instance of Example, then e.bar does not give you the instance of property assigned to Example.bar; it gives you the result of Example.bar.__get__(e, Example) (which in this case, happens to be Example.bar.fget(e), where fget is the original function decorated by #property).
In short, every instance has its own _bar attribute, but access to that attribute is mediated by the class attribute Example.bar.
It's easier to see that bar is a class attribute if you write this minimal (and sufficient, since neither the getter nor setter in this case requires a def statement) definition.
class Example:
def __init__(self):
self.bar = "initalizedValue"
bar = property(lambda self: self._bar, lambda self, b: setattr(self, '_bar', b))
or more generally
def bar_getter(self):
return self._bar
def bar_setter(self, b):
self._bar = b
class Example:
def __init__(self):
self.bar = "initalizedValue"
bar = property(bar_getter, bar_setter)

How to override a method of a member object?

In python 3.4 I have a member object through composition.
I would like to override one of it's member functions.
def class Foo:
def __init__(self, value):
self.value = value
def member_obj.baz(baz_self, arg):
print("my new actions on {}".format(arg))
Foo.member_obj.baz(arg) #the original function
foo_inst = Foo(2)
bar = Bar(*bar_parameters) #from a third party module
setattr(foo_inst, "member_obj", bar) #it did not "stick" when I did foo_inst.member_obj = bar
foo_inst.member_obj.baz("some argument")
It does not make sense to inherit from the Bar class.
I also only want this different behaviour to occur if the object is inside Foo. I use Bar in many other places and would like to retain the same way of calling the method. I.e. I would like to avoid wrapping it in Foo.baz.
Is it even possible to do something like the def member_obj.baz and is it a good idea?
It would be similar to this: https://softwareengineering.stackexchange.com/questions/150973/what-are-the-alternatives-to-overriding-a-method-when-using-composition-instea
Are you trying to do something like this?
class B():
def __init__(self):
self.x = None
def fun(self):
print("Assigning value to attribute of object of class B.\n")
self.x = "Value of B object's attribute"
class A():
def __init__(self):
self.value = B()
def fun(self):
print("Screw this, I'll do something else this time!\n")
self.value.x = 13
def override(self):
# Edit: you can assign any identifier (that is not reserved) to
# any type of object or method AND the "fun" ("really self.fun")
# above is visible from here, since we passed "self" as an
# argument
self.value.fun = self.fun
myObj = B()
myOtherObj = A()
myOtherObj.override()
myObj.fun()
myOtherObj.value.fun()

Python: How to do extra stuff when a specific attribute of an object is accessed?

Let's say I have a class in Python:
class Foo(object):
a = 1
b = 2
I'd like to do some extra stuff when I access 'a' but NOT 'b'. So, for example, let's assume that the extra stuff I'd like to do is to increment the value of the attribute:
> f = Foo()
> f.a # Should output 2
> f.a # Should output 3
> f.a # Should output 4
> f.b # Should output 2, since I want the extra behavior just on 'a'
It feels like there is a way through __getattr__ or __getattribute__, but I couldn't figure that out.
The extra thing can be anything, not necessarily related to the attribute (like print 'Hello world').
Thanks.
What you are looking for is a property, which can be used nicely as a decorator:
class Foo(object):
_a = 2
#property
def a(self):
Foo._a += 1
return Foo._a - 1
b = 2
The function is called whenever you try to access foo_instance.a, and the value returned is used as the value for the attribute. You can also define a setter too, which is called with the new value when the attribute is set.
This is presuming you want the odd set-up of class attributes you only ever access from instances. (_a and b here belong to the class - that is, there is only one variable shared by all instances - as in your question). A property, however, is always instance-owned. The most likely case is you actually want:
class Foo(object):
def __init__(self):
self._a = 2
self.b = 2
#property
def a(self):
self._a += 1
return self._a - 1
Where they are instance attributes.
If you really do want the equivalent of #property for a class variable, you have to build the descriptor yourself.
You almost certainly don't want to do this—see Lattyware's answer for how to make normal instance variables, and turn one of them into a #property.
But here's how you could do it:
class IncrementOnGetDescriptor(object):
def __init__(self, initval=None):
self.val = initval
def __get__(self, obj, objtype):
self.val += 1
return self.val - 1
def __set__(self, obj, val):
self.val = val
class Foo(object):
a = IncrementOnGetDescriptor(2)
b = 2
Now you can test it:
>>> f = Foo()
>>> f.a
2
>>> Foo.a
3
>>>> f.a
4
Turning this into a #classproperty decorator is left as an exercise for the reader.
PS, this still isn't exactly like a normal class variable. Setting Foo.a = 10 will replace your magic auto-incrementing value with a normal 10, while setting foo.a = 10 will update the class with an auto-incrementing 10 instead of storing an instance variable in f. (I originally had the __set__ method raise AttributeError, because normally you'd want an auto-incrementing magic variable be read-only, but I decided to show the more complex version just to show all the issues you have to deal with.)

What better way to get around the "static variables" in Python?

It seems that in Python, to declare a variable in a class, it is static (keeps its value in the next instances). What better way to get around this problem?
class Foo():
number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
1
Variables defined at the class level are indeed "static", but I don't think they work quite the way you think they do. There are 2 levels here which you need to worry about. There are attributes at the class level, and there are attributes at the instance level. Whenever you do self.attribute = ... inside a method, you're setting an attribute at the instance level. Whenever python looks up an attribute, it first looks at the instance level, if it doesn't find the attribute, it looks at the class level.
This can be a little confusing (especially if the attribute is a reference to a mutable object). consider:
class Foo(object):
attr = [] #class level attribute is Mutable
def __init__(self):
# in the next line, self.attr references the class level attribute since
# there is no instance level attribute (yet)
self.attr.append('Hello')
self.attr = []
# Now, we've created an instance level attribute, so further appends will
# append to the instance level attribute, not the class level attribute.
self.attr.append('World')
a = Foo()
print (a.attr) #['World']
print (Foo.attr) #['Hello']
b = Foo()
print (b.attr) #['World']
print (Foo.attr) #['Hello', 'Hello']
As others have mentioned, if you want an attribute to be specific to an instance, just initialize it as an instance attribute in __init__ (using self.attr = ...). __init__ is a special method which is run whenever a class is initialized (with a few exceptions that we won't discuss here).
e.g.
class Foo(object):
def __init__(self):
self.attr = 0
Just leave the declaration out. If you want to provide default values for the variables, initialize them in the __init__ method instead.
class Foo(object):
def __init__(self):
self.number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
0
Edit: replaced last line of the above snippet; it used to read 1 although it was just a typo on my side. Seems like it has caused quite a bit of confusion while I was away.
You maybe want to change the class attribute:
class Foo():
number = 0
def set(self):
Foo.number = 1
instead of overriding it!

Categories

Resources