Why class atribute doesn't change on update - python-3.x

I am wondering why the class attribute doesn't change in the code below. As you can see the value remains the same, despite being changed in class A?
class A:
valueA = 1.05
class User:
def __init__(self,name):
self.name = name
self.value = A.valueA
user = User('Alice')
print(user.value)
A.valueA = 1.1
print(A.valueA)
print(user.value)
output:
1.05
1.1
1.05

I don't understand why it should? ValueA is an number which is an immutable object(everything is a object), so valueA is just some sort of referencethat points to value 1.05.
To make more clear, here is an example of how does it behave:
class A(object):
val = [1,2,3]
class B(object):
def __init__(self):
self.myval = A.val
print(A.val)
# prints [1,2,3]
obj = B()
print(obj.myval)
# prints [1,2,3] because its starts to point to the same list
A.val[0] = 5
print(obj.myval)
# prints [5,2,3] because both still points to the same list,
# and you just changed it fist value
A.val = [4,5,6,7]
print(A.val)
# prints new list [4,5,6,7]
print(obj.myval)
# prints [5,2,3] because it still points to old list.
obj2 = B()
print(obj2.myval)
# prints new list [4,5,6,7] because assignment was done after A.val changed
also here is a good article about variables in python https://realpython.com/python-variables/#object-references

Related

Why am I getting this error? AttributeError: type object 'bubbleSort' has no attribute 'array'

I'm trying to make my own bubble-sort algorithm for learning purposes. I'm doing it by:
Making a random array
Checking if the first two indexes of the array need to be swapped
it does this throughout the whole list
and does it over and over until when looping through until the end it doesn't need to swap anything anymore then the loop breaks
but when I print any variable in the class it says that the class has no attribute of the variable.
this is my code right now
from random import randint
class bubbleSort:
def __init__(self, size):
self.size = size # Array size
self.array = [] # Random array
self.sorted = self.array # Sorted array
self.random = 0 # Random number
self.count = 0
self.done = False
self.equal = 0
while self.count != self.size:
random = randint(1, self.size)
if random in self.array:
pass
else:
self.array.append(random)
self.count += 1
def sort(self):
while self.done != True:
self.equal = False
for i in range(self.size):
if i == self.size:
pass
else:
if self.sorted[i] > [self.tmp]:
self.equal += 1
if self.equal == self.size:
self.done = True
else:
self.sorted[i], self.sorted[i + 1] = self.sorted[i+1], self.sorted[i]
new = bubbleSort(10)
print(bubbleSort.array)
This is what outputs
Traceback (most recent call last):
File "/home/musab/Documents/Sorting Algorithms/Bubble sort.py", line 38, in <module>
print(bubbleSort.array)
AttributeError: type object 'bubbleSort' has no attribute 'array'
In your case, you have a class called bubbleSort and an instance of this class called new, which you create using new = bubbleSort(10).
Since bubbleSort only refers to the class itself, it has no knowledge of member fields of any particular instance (the fields you create using self.xyz = abc inside of the class functions. And this is good, imagine having two instances
b1 = bubbleSort(10)
b2 = bubbleSort(20)
and you want to access the array of b1, you need to specify this somehow. The way to do it is to call b1.array.
Therefore, in your case you need to print(new.array).
bubbleSort is a class type, each object of this class type has its own array. To access array, one must do it through a class object. __init__ is called when creating a class object.
give the following a try:
bubbleSortObj = bubbleSort(10) # create a bubbleSort object
print(bubbleSortObj.array) # print the array before sort
bubbleSortObj.sort() # sort the array
print(bubbleSortObj.array) # print the array after sort
Notes
In __init__ you've got:
self.array = [] # Random array
self.sorted = self.array # Sorted array
In this case, array and sorted point to the same list and changing one would change the other. To make a copy of a list, one approach (among many) is to call sorted = list(array)
If there are any local function variables you can remove the self, eg, self.count = 0 can just be count = 0, as it's not needed again once it's used, and doesn't need to be a class member

Can I store a function rather than its result to a dictionary value so that it is dynamically updated when the value is called?

I have a dictionary:
mydict = {'x':val1,'x2':val2}
I would like to do something like
mydict['x3'] = some_func(mydict['x1'])
Where if I later change mydict['x1'] and call mydict['x3'], I return the output of some_func() with the new value of x1.
I realize this is something class property decorators handle well. I'd like to also be able to change the function stored at the 'x3' key for that specific object. This is something class properties cannot do to my knowledge (having the underlying function definition changed on an instance of the class).
You can just set 'x3' to a function in your dict. Then whenever you want to use it, you can call it. Something like this:
mydict = {'x1': val1,'x2': val2, 'x3': lambda: some_func(mydict['x1'])}
Now, mydict['x3'] is a function.
x3 = mydict['x3']()
print(x3)
Here's a quick demo
At this point, you should define a class rather than using a dict directly.
class MyClass:
def __init__(self, x1, x2):
self.x1 = x1
self.x2 = x2
#property
def x3(self):
return some_func(self.x1)
mydict = {'x':val1,'x2':val2}
m = MyClass(mydict['x'], mydict['x2'])
assert m.x3 == some_func(m.x1)
Based on primarily on Rocket's answer, I came up with this test solution.
class Test:
def __init__(self, x, y):
self.x = x
self.y = y
def set_prop(self, name, func):
setattr(Test, name, property(fget=func))
test = Test(1,2)
### Print list of class attributes in __dict__
for key, val in test.__dict__.items():
print(f"{key} = {val}")
### Create new property, xy defined by self.x * self.y
test.set_prop(name='xy', func=lambda self: self.x*self.y)
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test if it is properly linked to changes in x
test.x = 2
print(f"test.x set to {test.x}")
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test changing the underlying property formula
test.set_prop('xy',lambda self: self.x/self.y)
print(f"test.x ({test.x}) / test.y ({test.y}) = {test.xy}")

Write class such that calling instance returns all instance variables

I have answered my own question - see answer below
I'm writing a class, and I want this behavior:
a = f(10,20)
some_funct(a.row) # some_function is given 10
some_funct(a.col) # some_function is given 20
some_funct(a) # some_function is given a tuple of 10, 20 <-- THIS ONE :)
The last behavior is stumping me. I have not seen any examples that cover this.
Thus far:
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
Explictly I do not want another method, say, self.both = row, col.
I just want to 'call' the instance
I'm new to classes, so any improvements are welcome. Properties, setters, getters etc.
EDIT 1:
Replaced "print" with "some_function" in the question, and modified title
You can do like this
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def __str__(self):
return f"row = {row}, col = {col}"
and print like this
a = f(10,20)
print(a) # row = 10, col = 20
This might help
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def some_funct(self):
return (self.row, self.col)
You can access like
a = f(10,20)
a.some_funct() # (10, 20)
# or
row, col = a.some_funct()
From python 3.7 dataclasses have been introduced and their goal is to create classes that mainly contains data. Dataclasses comes with some helper function that extract the class attributes dict/tuples. e.g.
from dataclasses import dataclass,asdict,astuple
#dataclass
class f:
x: int
y: int
f_instance = f(10,20)
asdict(f_instance) # --> {'x': 10, 'y': 20}
astuple(f_instance) # -> (10,20)
EDIT I : Another technique would be to use namedtuple e.g.:
from collections import namedtuple
f = namedtuple('p',['row','col'])
a =f(10,20)
a.row #-> 10
a.col #-> 20
class f(tuple):
"""Simple 2d object"""
def __new__(cls, x, y):
return tuple.__new__(f, (x, y))
def __init__(self, x, y):
self.col = x
self.row = y
foo = f(1,2)
print(foo.col)
>>>1
print(foo.row)
>>>2
print(foo)
>>>(1, 2)
Importantly:
If you want it to behave like a tuple then make it a subclass of tuple.
Much stuffing around but stumbled upon an external site which gave me clues about the keywords to search on here. The SO question is here but I have modified that answer slightly.
I'm still a little confused because the other site says to use new in the init as well but does not give a clear example.

Can't change instance attribute of my class?

I'm starting out in python and I can't quite figure out why I'm unable to change the data stored in one of my instance attributes. I have the following code:
class Bank:
def __init__(self, bank = 'Unnamed'):
self.bank = bank
self.clients = []
self.status = {'bank': self.bank, 'clients': self.clients}
self.c_counter=0
def deposit(self, name = None, amount = 200):
self.name = name
self.amount = amount
self.c_counter += 1
if self.name is None:
self.name = print("client" + str(self.c_counter));
self.clients.append((self.name, self.amount))
else:
self.clients.append((self.name, self.amount))
This produces the following output:
bb = Bank("bb")
bb.deposit(amount = 2000)
bb.status
out: {'bank': bb, 'clients': [(None, 2000)]}
While the desired output is:
out: {'bank': bb, 'clients': [('client1', 2000)]}
As you can see, what I'm trying to do is to set the client name to "clientx" if client name is not specified during a deposit; the x is just a number to distinguish each generic client from other generic clients without a specific name.
If a specific name is not provided when calling the deposit attribute of the bank the client name is set to None by default. I check this with the if condition and change the client name accordingly, but for some reason the client name literally gets added as "None" to the list of clients in the dictionary (list of tuples). What is wrong here?
print prints a string to sys.stdout and returns None. you should change the line
self.name = print("client" + str(self.c_counter));
to
self.name = "client" + str(self.c_counter)
or maybe self.name = f"client{self.c_counter}" for python >= 3.6.
print is a NoneType, it's None and you can prove it:
>>> type(print())
<class 'NoneType'>
>>>
And:
>>> print(print())
None
>>>
So print isn't be used for assigning, print is used for outputting stuff, whereas in this case you're assigning stuff, that which isn't meant for print to handle, so change:
self.name = print("client" + str(self.c_counter));
To:
self.name = "client" + str(self.c_counter)
Or:
self.name = "client%s" % self.c_counter
Or:
self.name = "client{}".format(self.c_counter)
Or if your python version is bigger or equal to 3.6, you can use:
self.name = f"client{self.c_counter}"

Printing family tree until certain level | Python 3

I am struggling with a recursive function that prints a family tree until a certain "depth/level".
I have defined class "Person" and each person has some descendant(s), so lets say:
>>> george.children
[<__main__.Person object at 0x000002C85FB45A58>]
>>> george.name
'George'
And I want to print the family tree in a way that each generation is separated by 4 whitespaces, for example:
>>> family_tree(george, level = 2)
George
Michael
Juliet
Peter
Mary
George is the level 0, then his two sons are level 1, etc.
Do you please have any ideas how to write this using recursion? I would greatly appreciate it.
You could use recursion. At each deeper level of recursion you should produce 4 more spaces. So for that purpose you could pass an argument depth that is increased at every recursive call.
Here is how you could do it:
# You'll have a class like this:
class Person:
def __init__(self, name):
self.name = name
self.children = []
def addChild(self, child):
self.children.append(child)
return self # to allow chaining
# The function of interest:
def family_tree(person, level = 2):
def recurse(person, depth):
if depth > level: return
print (" " * (4 * depth) + person.name)
for child in person.children:
recurse(child, depth+1)
recurse(person, 0)
# Sample data
george = Person("George").addChild(
Person("Michael").addChild(
Person("Juliet").addChild(
Person("don't show me")
)
)
).addChild(
Person("Peter").addChild(
Person("Mary")
)
)
# Call the function
family_tree(george)

Resources