I know that python pass object by reference, but why the second output of codes below is 3 other than 10?
class a():
def __init__(self, value):
self.value = value
def test(b):
b = a(10)
b = a(3)
print(b.value)
test(b)
print(b.value)
Python objects are passed by value, where the value is a reference. The line b = a(3) creates a new object and puts the label b on it. b is not the object, it's just a label which happens to be on the object. When you call test(b), you copy the label b and pass it into the function, making the function's local b (which shadows the global b) also a label on the same object. The two b labels are not tied to each other in any way - they simply happen to both be currently on the same object. So the line b = a(10) inside the function simply creates a new object and places the local b label onto it, leaving the global b exactly as it was.
You did not return a value from the function to put into the class. This means the 'b' is irrelevant and does nothing. The only connection is the name 'b'
You need to reassign the value 'b' to be able to call the class.
class a():
def __init__(self, value):
self.value = value
def test(b):
b = a(10)
return b
b = a(3)
print(b.value)
b = test(3)
print(b.value)
Related
Consider the following minimal example for my question:
class MyClass:
a = False
b = 0
if a:
b = 1
MCinst = MyClass()
Here MCinst has two attributes, MCinst.a and MCinst.b with default values False and 0 respectively. What I was trying to achieve with the if statement, is that the value of MCinst.b would automatically switch to 1 when I set MCinst.a = True. But this obviously does not work as intended, since the value of MCinst.b stays 0.
I know that I could remove the if statement and simply modify b from the outside, in the same way in which I modified a. But I am still curious if there is a way to make work what I wanted—i.e. to have an attribute of an instance change automatically when I change another attribute of the same instance.
class MyClass:
a = False
b = 0
def __setattr__(self, key, value):
if key == 'a':
setattr(self, 'b', 1)
super().__setattr__(key, value)
If you actually want to change the class attribute, you can use
setattr(self.__class__, 'b', 1)
Suppose that I have a function like this one:
def foo():
a = 1
b = 2
c = 3
return c
Now can I use the a, bvariables? Even if foo() is not returning them like this?
foo().a + foo().b
I know this is easy to do if foo was a class, but it's not.
You can try function attributes. We know that everything in python is an object. So you can define attributes for a function also since a function is also an object. If you want to use a local function variable outside the function, then it is better to make those variables as the function attributes.
I tried something like below :
def foo():
foo.a = 1 # function attribute
foo.b = 2 # function attribute
c = 3
return c
foo()
print(foo.a + foo.b) # -> 3
Even though you are saying you have a function, not a class, python is able to transcend those differences using callable classes. Those are normal classes whose instances can be called as normal functions while still maintaining an internal state. Here is a demonstration:
class Foo:
def __init__(self):
self.a = self.b = self.c = None
def __call__(self):
self.a = 1
self.b = 2
self.c = 3
return self.c
foo = Foo()
print(foo()) # will print 3
print(foo.a) # will print 1
print(foo.b) # will print 2
In the above code snippet, you can think of Foo as a class that generates objects (int this case foo), each being a function. Your code can call foo() and what this will do, is execute the __call__() method of that class. Since however __call__() uses self.a, self.b, self.c instead of local variables a, b, c, their values after the execution of the function will be saved inside the foo object. So you can check them just by doing foo.a and so on.
If you would like to add more arguments in your original foo() function, simply add them to the __call__() method and assign values to them when calling foo().
You cannot use them like that.You can return all 3 variables and use them accordingly.
Try:
def foo():
a = 1
b = 2
c = 3
return a,b,c
a,b,c = foo()
Alternatively you can create function variables as below.
def foo():
foo.a = 1
foo.b = 2
foo.c = 3
foo()
print(foo.a, foo.b, foo.c)
For example,
class someClass(object):
def __init__(self):
self.a = np.array([3])
b = self.a
self.a += 2
Then both a and b are 5. But what I want is that b remains 3. So I want to assign to b only the value stored in the instance variable a. Is there any way that I can achieve it?
I have a class which contains data as attributes and which has a method to return a tuple containing these attributes:
class myclass(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def tuple(self):
return (self.a, self.b, self.c)
I use this class essentially as a tuple where the items (attributes) can be modified/read through their attribute name. Now I would like to create objects of this class, which would be constants and have pre-defined attribute values, which I could then assign to a variable/mutable object, thereby initializing this variable object's attributes to match the constant object, while at the same time retaining the ability to modify the attributes' values. For example I would like to do this:
constant_object = myclass(1,2,3)
variable_object = constant_object
variable_object.a = 999
Now of course this doesn't work in python, so I am wondering what is the best way to get this kind of functionality?
Now I would like to create objects of this class, which would be constants and have pre-defined attribute values, which I could then assign to a variable/mutable object, thereby initializing this variable object's attributes to match the constant object,
Well, you can't have that. Assignment in Python doesn't initialize anything. It doesn't copy or create anything. All it does is give a new name to the existing value.
If you want to initialize an object, the way to do that in Python is to call the constructor.
So, with your existing code:
new_object = myclass(old_object.a, old_object.b, old_object.c)
If you look at most built-in and stdlib classes, it's a lot more convenient. For example:
a = set([1, 2, 3])
b = set(a)
How do they do that? Simple. Just define an __init__ method that can be called with an existing instance. (In the case of set, this comes for free, because a set can be initialized with any iterable, and sets are iterable.)
If you don't want to give up your existing design, you're going to need a pretty clumsy __init__, but it's at least doable. Maybe this:
_sentinel = object()
def __init__(myclass_or_a, b=_sentinel, c=_sentinel):
if isinstance(a, myclass):
self.a, self.b, self.c = myclass_or_a.a, myclass_or_a.b, myclass_or_a.c
else:
self.a, self.b, self.c = myclass_or_a, b, c
… plus some error handling to check that b is _sentinel in the first case and that it isn't in the other case.
So, however you do it:
constant_object = myclass(1,2,3)
variable_object = myclass(constant_object)
variable_object.a = 999
import copy
class myclass(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def tuple(self):
return (self.a, self.b, self.c)
constant_object = myclass(1,2,3)
variable_object = copy.deepcopy(constant_object)
variable_object.a = 999
print constant_object.a
print variable_object.a
Output:
1
999
Deepcopying is not entirely necessary in this case, because of the way you've setup your tuple method
class myclass(object):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
def tuple(self):
return (self.a, self.b, self.c)
constant_object = myclass(1,2,3)
variable_object = myclass(*constant_object.tuple())
variable_object.a = 999
>>> constant_object.a
1
>>> variable_object.a
999
Usually (as others have suggested), you'd want to deepcopy. This creates a brand new object, with no ties to the object being copied. However, given that you are using only ints, deepcopy is overkill. You're better off doing a shallow copy. As a matter of fact, it might even be faster to call the class constructor on the parameters of the object you already have, seeing as these parameters are ints. This is why I suggested the above code.
Im trying this piece of code below but wondering why its not working..
Can you please let me know wht is the problem,
code.py
class Example():
def A1(self):
return self.B1() + self.B2()
def B1(self):
return 4 * self.C1()
def B2(self):
return 5
def C1(self):
return 2
def main():
spreadsheet = Example()
print spreadsheet.A1()
spreadsheet.C1 = 3
print spreadsheet.A1()
C1 starts out as a method - you call .A1(), which calls .B1(), which calls .C1(), which returns 2. So far, so good.
You then make .C1 a value (3) - you call .A1(), which calls .B1(), which tries to call .C1(), which blows up because the value 3 is not callable.
Maybe you want something like
class Spreadsheet(object):
def __getattribute__(self, cell):
val = object.__getattribute__(self, cell)
return val(self) if callable(val) else val
def main():
s = Spreadsheet()
s.a1 = lambda self: self.b1 + self.b2
s.b1 = lambda self: 4 * self.c1
s.b2 = 5
s.c1 = 2
print s.a1 # => 13
s.c1 = 3
print s.a1 # => 17
In your class, the variable C1 was just a variable that held an instance method. Setting it equal to 3 overrides the function.
For example:
def foo():
print 4
foo = 12 # foo is now the number 12, not a function
You did the same thing:
spreadsheet.C1 = 3
C1 was a function, but now it's a number. You can't call a number.
You mean that if you execute main you get a TypeError when it gets to print spreadsheet.A1()
It is because you overwrite the instance variable C1. It was assigned to a function, but then you reassign it to the integer 3. When it tries to process the instance method A1 it eventually tries to call self.C1() and finds an integer, which can't be called and shouldn't have the parenthesis after it, so it properly throws an error.
Depending on what you want to happen you have several options.
Accept that this is normal behavior and don't overwrite instance procedures.
Define a setter for C1 that would either return an error at that point or silently do nothing.
The name "C1" refers to a method (a callable object). But then you assign "C1" attribute to an integer (not callable), and clobber the method. Then when you call it with self.C1() it won't work anymore.