How can something be added to an attribute? - python

How can something be modified or added to a class?
For example, in the following code what does it mean that hello can modify the a I used in __init__? I still have a lot of uncertainty on OOP, especially the relation between instantiation and other things.
I'm sure it's possible; if so, how can I do it?
class Foo:
def __init__(self, a):
self.a = a
def hello(self):

You are already successfully adding/modifying the class instance. Namely, __init__ takes some a and adds it as the attribute .a to the instance self. Using the same name for the variable and attribute is incidental -- you could as well use different names:
class Bla:
def __init__(self, b):
self.a = b # add `b` as attribute `a`
Once an attribute is added to an instance, this attribute can by default be read and overridden freely. This applies to the initial method, other methods, and any other function/method having access to the instance.
class Bla:
def __init__(self, b):
self.a = b # add `b` as attribute `a`
def lol(self, c):
self.a = c # add `c` as attribute `a` (discarding previous values)
def rofl(self, d):
self.a += d # re-assign `self.a + d` as attribute `a`
# external function modifying `Bla` instance
def jk(bla, e):
bla.a = e # add `e` as attribute `a` (discarding previous values)

I'm not sure how Bla.lol() can modify the argument a that is supplied to it, but it can alter the attribute Bla.a or self.a as it is referenced from within the object, which initially has the value of argument a assigned to it.
We can have the function lol assign a new value to the attribute a, in this case I'm going to assume a is a string, and extend the string with the phrase ' has been altered':
>>> class Bla:
... def __init__(self, a):
... self.a = a
...
... def lol(self):
... self.a += ' has been altered'
...
>>> instance = Bla('the arg a')
>>> instance.a # check a
'the arg a'
>>> instance.lol() # modifies a
>>> instance.a # check a again
'the arg a has been altered'
>>> instance.lol() # we can alter it again, as many times as we run the function
>>> instance.a # and see it has been altered a second time
'the arg a has been altered has been altered'

Related

call method on an arbitrary attribute in user-defined class

Ok, this is probably really simple but I'm struggling. So, let's say I have a simple class "Simple" that has some attributes a and b. And I have a method, "trim" that can be used on one attribute at a time:
class Simple():
def __init__(self, a=None, b=None):
self.a = a
self.b = b
def trim(self.[???]):
...do some stuff...
return self
Now, I want the user to be able to apply the method to any of the attributes (i.e., a and b) with something like:
simple_with_a_trimmed = simple.trim('a')
or
simple_with_b_trimmed = simple.trim('b')
How can I set up the method so that I can pass it the appropriate attribute? Thanks in advance for any thoughts!
You can use the get getattr and setattr methods. These allow you to refer to a class attribute by a string.
You can pass the desired attribute to the trim method. You can then retrieve the value of the specified attribute using getattr. Then, you can either transform it, and return it, or perhaps modify it using setattr -- to update the object.
Below is a simple example of this in action. The trim1 method merely returns the value of the (transformed) attribute but does not update the object. The trim2 method changes the object itself -- it updates the value of the specified attribute.
I've also added a __str__ method to show how the object changes after each call.
class Simple:
def __init__(self, a=None, b=None):
self.a = a
self.b = b
def trim1(self, attr):
# Get value of the attribute
current_value = getattr(self, attr)
# Transform and return the value of the attribute
# Removing whitespace as an example
return current_value.strip()
def trim2(self, attr):
# Get value of the attribute
current_value = getattr(self, attr)
# Transform the value of the attribute
# Removing whitespace as an example
current_value = current_value.strip()
# Update the value of the attribute
setattr(self, attr, current_value)
def __str__(self):
return f'a: "{self.a}", b: "{self.b}"'
# Create an object of type Simple
simple = Simple(' data1', 'data2 ')
# These methods do not change the object, but return the transformed value
a = simple.trim1('a')
b = simple.trim1('b')
print(f'Returned value of a: "{a}"')
print(f'Returned value of b: "{b}"')
print(simple) # Prints `a: " data1", b: "data2 "`
# Apply the transformation to attribute `a`
simple.trim2('a')
print(simple) # Prints `a: "data1", b: "data2 "`
# Apply the transformation to attribute `b`
simple.trim2('b')
print(simple) # Prints `a: "data1", b: "data2"`

One instance attribute referencing another, with updates after class instantiation

I am looking for best practices on setting one instance attribute that references another instance attribute after the class has been instantiated.
For example:
class Foo:
def __init__(self):
self.a = 1
self.b = self.a + 1
>>> obj_foo = Foo()
>>> obj_foo.a
1
>>> obj_foo.b
2
>>> obj_foo.a = 5
>>> obj_foo.a
5
>>> obj_foo.b
2 # I want this to be 6
Is this bad practice for one instance attribute to reference another?
I can see how implementing a method to check for and update dependent instance attributes, but this seems like a lot of overhead/hacky. Any assistance is greatly appreciated!
It seems like you don't actually want to store the value of b at all, but instead want to generate it based on the value of a dynamically. Luckily, there's a property class/decorator that you can use just for this purpose:
class Foo:
def __init__(self, a=1):
self.a = a
#property
def b(self):
return self.a + 1
This will create a read-only property b that will behave just like a normal attribute when you access it as foo.b, but will a because it is a descriptor. It will re-compute the value based on whatever foo.a is set to.
Your fears about calling a method to do the computation every time are not entirely unjustified. Using the . operator already performs some fairly expensive lookups, so your toy case is fine as shown above. But you will often run into cases that require something more than just adding 1 to the argument. In that case, you'll want to use something like caching to speed things up. For example, you could make a into a settable property. Whenever the value of a is updated, you can "invalidate" b somehow, like setting a flag, or just assigning None to the cached value. Now, your expensive computation only runs when necessary:
class Foo:
def __init__(self, a=1):
self._a = a
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
self._b = None
#property
def b(self):
if self._b is None:
# Placeholder for expensive computation here
self._b = self._a + 1
return self._b
In this example, setting self.a = a in __init__ will trigger the setter for the property foo.a, ensuring that the attribute foo._b always exists.

Python referencing instance list variable

I just started to learn Python and I"m struggling a little with instance variables. So I create an instance variable in a method that's of a list type. Later on, I want to call and display that variable's contents. However, I'm having issues doing that. I read some online, but I still can't get it to work. I was thinking of something along the following (this is a simplified version):
What would the proper way of doing this be?
class A:
def _init_(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal():
#Is this needed? Likewise a "get" method"?
def randomMethod():
A.listVar[0] #something like that to call/display it right? Or would a for
#for loop style command be needed?
Class B:
def _init_(self):
self.a = ""
self.b = ""
self.c = ""
Is the list something you'll be passing to the instance when you create it (i.e. will it be different each time)?
If so, try this:
class A:
def __init__(self, list):
self.listVar = list
Now, when you instantiate (read: create an instance) of a class, you can pass a list to it and it will be saved as the listVar attribute for that instance.
Example:
>>> first_list = [B("1","2","3"), B("1","2","3")]
>>> second_list = [C("1","2","3"), C("1","2","3")]
>>> first_instance = A(first_list) # Create your first instance and pass it your first_list. Assign it to variable first_instance
>>> first_instance.listVar # Ask for the listVar attribute of your first_instance
[B("1","2","3"), B("1","2","3")] # Receive the list you passed
>>> second_instance = A(second_list) # Create your second instance and pass it your second_list. Assign it to variable second_instance
>>> second_instance.listVar # Ask for the listVar attribute of your second_instance
[C("1","2","3"), C("1","2","3")] # Receive the list you passed second instance
Feel free to ask if anything is not clear.
class A:
def __init__(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal(self, val):
self.listVar[0] = val # example of changing the first entry
def randomMethod(self):
print self.listVar[0].a # prints 'a' from the first entry in the list
class B:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
I made several changes. You need to use self as the first argument to all the methods. That argument is the way that you reference all the instance variables. The initialization function is __init__ note that is 2 underscores before and after. You are passing three arguments to initialize B, so you need to have 3 arguments in addition to self.

Method namespaces/collections in Python classes

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.

bound methods in python

>>> class Class:
... def method(self):
... print 'I have a self!'
...
>>> def function():
... print "I don't..."
...
>>> instance = Class()
>>> instance.method()
I have a self!
>>> instance.method = function
>>> instance.method()
I don't...
Okay the book quotes 'The self parameter (mentioned in the previous section) is, in fact, what distinguishes methods
from functions. Methods (or, more technically, bound methods) have their first parameter
bound to the instance they belong to: you don’t have to supply it. So while you can certainly
bind an attribute to a plain function, it won’t have that special self parameter:'
I am not able to understand what the author is trying to convey here ! I am new to oop in python . Please explain me .
Methods only exist on the class; assigning the function to the instance attribute as your example does creates an instance attribute containing the function, and not a method.
It means that affect of
class A:
def a(self):
print 'a'
def b(self, arg):
print arg
can be roughly represented by:
def A_a(self):
print a
def A_b(self, arg):
print arg
class A:
def __init__(self):
self.a = lambda: A_a(self)
self.b = lambda arg: A_b(self, arg)
So instance.a is not original function a which is written in class A, but another function which calls original with additional self argument.

Categories

Resources