Python B-tree: every node hangs from root - python

Ok, basically what I am trying to do is to have implemented a very basic and simple B-tree, but I am finding myself with a problem: the root node holds as a child every node in the tree. I have simplified as much as possible, and here is an example of the problem that I am facing:
>>> class Btree():
subs = []
value = 0
>>> a=Btree()
>>> b=Btree()
>>> c=Btree()
>>> a.value=1
>>> b.subs.append(a)
>>> b.value=2
>>> c.subs.append(b)
>>> c.value = 3
>>> c
<__main__.variable object at 0x103d916a0>
>>> c.value
3
>>> len(c.subs)
2
>>> c.subs
[<__main__.variable object at 0x103d91780>, <__main__.variable object at 0x103d917f0>]
>>> c.subs[1].value
2
>>> c.subs[0].value
1
Can somebody tell me what is going on?

You need to use an __init__() method to make instance attributes. The way you are doing it now you are declaring class attributes that are shared by all instances of the class. Try doing this instead:
class BTree:
def __init__(self):
self.subs = []
self.value = 0

Related

python multiprocessing.Processā€˜s object name of class

if __name__ == '__main__':
for i in range(5):
p = Process(target=f, args=('bob',))
p.start()
I can't understand why python can create many same object name of class.
Python doesn't declare variables like that, it creates names and then uses reference counting to account for how many names refer to an object
This works the other way too, for example a list with the names a and b can be modified via either
>>> a = []
>>> b = a
>>> b.append(1)
>>> a
[1]
As even basic types are instances of objects, you can see this reference counting at work with even a humble int
>>> import sys
>>> a = 1
>>> sys.getrefcount(1)
219
>>> b = 1
>>> sys.getrefcount(1)
220
>>> del a
>>> sys.getrefcount(1)
219
Take a look at Python has "names" from this now-ancient, but venerable article for a great description
https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html#python-has-names

Class variable vs instance variable

While learning python through python docs, i came across the following wherein its explained that class variable is common to the class and that any object can change it:
Sample Code 1:
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
Output:
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
Question => If so, then why doesn't y in the following example get affected when x changes its tricks attribute to 5?
Sample Code 2:
class Complex:
tricks = 3
def __init__(self,var1):
self.tricks=var1
def add_tricks(self,var1):
self.tricks=var1
x = Complex(11)
y = Complex(12)
print (x.tricks)
print (y.tricks)
x.add_tricks(5)
print (x.tricks)
print (y.tricks) -->Remains unchanged
Output:
11
12
5
12 -->Remains unchanged
And what exactly is the difference when i remove the self in the following program:
Sample Code 3:
class Complex:
tricks = 3
def __init__(self,var1):
self.tricks=var1
def add_tricks(self,var1):
tricks=var1
x = Complex(11)
y = Complex(12)
print (x.tricks)
print (y.tricks)
x.add_tricks(5) -->This change is not reflected anywhere
print (x.tricks)
print (y.tricks)
print(Complex.tricks)
Output:
11
12
11
12
3
This example may be illustrative. Given the following class (I've dropped the initialiser from your example because it doesn't let us demonstrate the behaviour):
class Complex:
tricks = 3
def add_tricks(self, value):
self.tricks = value
We can see, upon creation, the value of their tricks attribute is both 3:
>>> a = Complex()
>>> b = Complex()
>>>
>>> a.tricks
3
>>> b.tricks
3
Let's take a second and look at the names defined on those objects:
>>> a.__dict__
{}
>>> b.__dict__
{}
They're both objects with no attributes themselves. Let's see what happens after we call add_tricks on b:
>>> b.add_tricks(5)
>>>
>>> a.tricks
3
>>> b.tricks
5
Okay. So, this looks like the shared value hasn't been affected. Let's take a look at their names again:
>>> a.__dict__
{}
>>> b.__dict__
{'tricks': 5}
And there it is. Assigning to self.tricks creates an attribute local to that object with name tricks, which when accessed via the object (or self) is the one that we'll use from that point forward.
The shared value is still there and unchanged:
>>> a.__class__.tricks
3
>>> b.__class__.tricks
3
It's just on the class, not on the object.

How do I get two instances of a class in Python that I can edit individually?

I know this is probably a repeat but beats me if I can find the answer on here. I want to be able to have different instances of a class that can hold different values. I've tried deep and shallow copies, I've tried copying the class proper, instances of the class, you name it.
>>> class classy:
... a = 1
... b = 2
...
>>> import copy
>>> x = copy.deepcopy(classy)
>>> y = copy.deepcopy(classy)
>>> x.a = 100
>>> y.a
100
So essentially I want y.a to return 1 instead of 100.
Thanks
Use instance variables. That's what they're for.
class classy:
def __init__(self):
self.a = 1
self.b = 2
x = classy()
y = classy()
x.a = 100
>>> print y.a
1
Jayanth has shown the way to do it. I just want to point out the key problem with your code.
Your question title asks about instances, but your code has no instances. You have only a class. If you want instances of a class, you need to make instances, which is done by calling the class:
>>> class classy:
... a = 1
... b = 2
>>> x = classy()
>>> y = classy()
>>> x.a = 100
>>> y.a
1
Note that Jayanth does this in his answer as well. The reason why the above code works, even without using instance attributes as shown in Jayanth's answer, is somewhat more complex, but the basic point is vital: instances and classes are two different things. You make instances from classes by calling the class. You should read the Python tutorial to familiarize yourself with classes and instances in Python.

Python create new object with the same value [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 15 days ago.
I come from java world where I expect following things
int a = valueassignedbyfunction();
int b = a;
a = a + 1;
after this a is 1 greater than b. But in python the b automatically gets incremented by one once the a = a + 1 operation is done because this b is referencing to the same object as a does. How can I copy only the value of a and assign it to a new object called b?
Thanks!
Assuming integers, I cannot reproduce your issue:
>>> a = 1
>>> b = a
>>> a += 1
>>> a
2
>>> b
1
If we assume objects instead:
class Test(object):
... def __init__(self, v):
... self.v = v
...
>>> a = Test(1)
>>> b = a.v
>>> a.v += 1
>>> print a.v, b
2 1
# No issues so far
# Let's copy the object instead
>>> b = a
>>> a.v += 1
>>> print a.v, b.v
3 3
# Ah, there we go
# Using user252462's suggestion
>>> from copy import deepcopy
>>> b = deepcopy(a)
>>> a.v += 1
>>> print a.v, b.v
4 3
I think the main confusion here is the following: In Java, a line like
int i = 5;
allocates memory for an integer and associates the name i with this memory location. You can somehow identify the name i with this memory location and its type and call the whole thing "the integer variable i".
In Python, the line
i = 5
evaluates the expression on the right hand side, which will yield a Python object (in this case, the expression is really simple and will yield the integer object 5). The assignment statement makes the name i point to that object, but the relation between the name and the object is a completely different one than in Java. Names are always just references to objects, and there may be many names referencing the same object or no name at all.
This documentation might help out: http://docs.python.org/library/copy.html
You can use the copy library to deepcopy objects:
import copy
b = copy.deepcopy(a)
I'm not sure what you're seeing here.
>>> a = 1
>>> b = a
>>> a = a + 1
>>> b
1
>>> a
2
>>> a is b
False
Python Integers are immutable, the + operation assigns creates a new object with value a+1. There are some weird reference issues with integers (http://distilledb.com/blog/archives/date/2009/06/18/python-gotcha-integer-equality.page), but you should get the same thing you expected in Java
How about just doing
a = 1
b = a*1

How should I declare default values for instance variables in Python?

Should I give my class members default values like this:
class Foo:
num = 1
or like this?
class Foo:
def __init__(self):
self.num = 1
In this question I discovered that in both cases,
bar = Foo()
bar.num += 1
is a well-defined operation.
I understand that the first method will give me a class variable while the second one will not. However, if I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more 'pythonic' than the other?
One thing I've noticed is that in the Django tutorial, they use the second method to declare Models. Personally I think the second method is more elegant, but I'd like to know what the 'standard' way is.
Extending bp's answer, I wanted to show you what he meant by immutable types.
First, this is okay:
>>> class TestB():
... def __init__(self, attr=1):
... self.attr = attr
...
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1
However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:
>>> class Test():
... def __init__(self, attr=[]):
... self.attr = attr
...
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>>
Note that both a and b have a shared attribute. This is often unwanted.
This is the Pythonic way of defining default values for instance variables, when the type is mutable:
>>> class TestC():
... def __init__(self, attr=None):
... if attr is None:
... attr = []
... self.attr = attr
...
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]
The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.
The two snippets do different things, so it's not a matter of taste but a matter of what's the right behaviour in your context. Python documentation explains the difference, but here are some examples:
Exhibit A
class Foo:
def __init__(self):
self.num = 1
This binds num to the Foo instances. Change to this field is not propagated to other instances.
Thus:
>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1
Exhibit B
class Bar:
num = 1
This binds num to the Bar class. Changes are propagated!
>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3
Actual answer
If I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more 'pythonic' than the other?
The code in exhibit B is plain wrong for this: why would you want to bind a class attribute (default value on instance creation) to the single instance?
The code in exhibit A is okay.
If you want to give defaults for instance variables in your constructor I would however do this:
class Foo:
def __init__(self, num = None):
self.num = num if num is not None else 1
...or even:
class Foo:
DEFAULT_NUM = 1
def __init__(self, num = None):
self.num = num if num is not None else DEFAULT_NUM
...or even: (preferrable, but if and only if you are dealing with immutable types!)
class Foo:
def __init__(self, num = 1):
self.num = num
This way you can do:
foo1 = Foo(4)
foo2 = Foo() #use default
Using class members to give default values works very well just so long as you are careful only to do it with immutable values. If you try to do it with a list or a dict that would be pretty deadly. It also works where the instance attribute is a reference to a class just so long as the default value is None.
I've seen this technique used very successfully in repoze which is a framework that runs on top of Zope. The advantage here is not just that when your class is persisted to the database only the non-default attributes need to be saved, but also when you need to add a new field into the schema all the existing objects see the new field with its default value without any need to actually change the stored data.
I find it also works well in more general coding, but it's a style thing. Use whatever you are happiest with.
With dataclasses, a feature added in Python 3.7, there is now yet another (quite convenient) way to achieve setting default values on class instances. The decorator dataclass will automatically generate a few methods on your class, such as the constructor. As the documentation linked above notes, "[t]he member variables to use in these generated methods are defined using PEP 526 type annotations".
Considering OP's example, we could implement it like this:
from dataclasses import dataclass
#dataclass
class Foo:
num: int = 0
When constructing an object of this class's type we could optionally overwrite the value.
print('Default val: {}'.format(Foo()))
# Default val: Foo(num=0)
print('Custom val: {}'.format(Foo(num=5)))
# Custom val: Foo(num=5)
Using class members for default values of instance variables is not a good idea, and it's the first time I've seen this idea mentioned at all. It works in your example, but it may fail in a lot of cases. E.g., if the value is mutable, mutating it on an unmodified instance will alter the default:
>>> class c:
... l = []
...
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
You can also declare class variables as None which will prevent propagation. This is useful when you need a well defined class and want to prevent AttributeErrors.
For example:
>>> class TestClass(object):
... t = None
...
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>
Also if you need defaults:
>>> class TestClassDefaults(object):
... t = None
... def __init__(self, t=None):
... self.t = t
...
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>
Of course still follow the info in the other answers about using mutable vs immutable types as the default in __init__.

Categories

Resources