How does del interact with object attributes? [duplicate] - python

This question already has answers here:
What does "del" do exactly?
(1 answer)
Variable scopes in Python classes
(4 answers)
Closed 5 years ago.
I'm new to Python and saw this code snippet:
class C:
abc = 2
c1 = C()
print c1.abc
c1.abc = 3
print c1.abc
del c1.abc
print c1.abc
I understand why the first and the second print statements print 2, respectively 3. Coming from a Java background however, I don't understand what happens in the line 'del c1.abc' and why the last print statement prints 2 and not some kind of an error. Can someone explain? If possible by comparing to Java?

The sticky issue to a Python beginner here is that abc is a class variable (i.e. a "static" variable), and when you do c1.abc = 3, you shadow the class variable with an instance variable. When you do del c1.abc the del applies to the instance variable, so calling c1.abc now returns the class variable.
The following interactive session should clear some things up:
>>> class C:
... abc = 2
...
>>> c1 = C()
>>> c2 = C()
>>> c1.abc = 3
>>> c1.abc
3
>>> c2.abc
2
>>> C.abc # class "static" variable
2
>>> del c1.abc
>>> c1.abc
2
>>> c2.abc
2
>>> C.abc
2
>>> del c2.abc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: C instance has no attribute 'abc'
>>> del C.abc
>>> c1.abc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: C instance has no attribute 'abc'
>>>
It is del.<someattribute> always deletes the instance attribute. It won't delete a class-level attribute if applied to an instance, instead, you have to apply it to the class!
In Python, everything written inside a class block is always at the class level. In this sense, it is simpler than Java. To define an instance variable, you need to assign directly to an instance, outisde a method (c1.abc = 3) or inside a method, using the first parameter passed to that method (by convention this is called self but could be banana if you wanted):
>>> class C:
... def some_method(banana, x): # by convention you should use `self` instead of `banana`
... banana.x = x
...
>>> c = C()
>>> c.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: C instance has no attribute 'x'
>>> c.some_method(5)
>>> c.x
5

Related

behavior of builtin functions when assigned as class attributes

I would like to assign a function as class attribute, and have it so when accessed through instance, it is still unbounded. I understand that this can be achieved with using staticmethod descriptor. But it seems the behavior is different for the builtin functions, and I would like to replicate that.
def abs_(value):
return abs(value)
class Test:
func_1 = abs
func_2 = len
func_3 = abs_
func_4 = staticmethod(abs_)
>>> test = Test()
>>> test.func_1
<built-in function abs>
>>> test.func_2
<built-in function len>
>>> test.func_3
<bound method abs_ of <__main__.Test object at 0x10436d910>>
In this case, the builtin function are unbound, and the defined function abs_ is bound to the instance. And obviously all functions work except func_3 since it is bound method.
>>> test.func_1(-1)
1
>>> test.func_3(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: abs_() takes 1 positional argument but 2 were given
How does builtin function achieve this, and is there a way to replicate the behavior (remain unbound)? Thank you!

How to use a DefaultDict with a lambda expression to make the default changeable?

DefaultDicts are useful objects to be able to have a dictionary that can create new keys on the fly with a callable function used to define the default value. eg. Using str to make an empty string the default.
>>> food = defaultdict(str)
>>> food['apple']
''
You can also use lambda to make an expression be the default value.
>>> food = defaultdict(lambda: "No food")
>>> food['apple']
'No food'
However you can't pass any parameters to this lambda function, that causes an error when it tries to be called, since you can't actually pass a parameter to the function.
>>> food = defaultdict(lambda x: "{} food".format(x))
>>> food['apple']
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
food['apple']
TypeError: <lambda>() takes exactly 1 argument (0 given)
Even if you try to supply the parameter
>>> food['apple'](12)
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
food['apple']
TypeError: <lambda>() takes exactly 1 argument (0 given)
How could these lambda functions be responsive rather than a rigid expression?
Using a variable in the expression can actually circumvent this somewhat.
>>> from collections import defaultdict
>>> baseLevel = 0
>>> food = defaultdict(lambda: baseLevel)
>>> food['banana']
0
>>> baseLevel += 10
>>> food['apple']
10
>>> food['banana']
0
The default lambda expression is tied to a variable that can change without affecting the other keys its already created. This is particularly useful when it can be tied to other functions that only evaluate when a non existant key is being accessed.
>>> joinTime = defaultdict(lambda: time.time())
>>> joinTime['Steven']
1432137137.774
>>> joinTime['Catherine']
1432137144.704
>>> for customer in joinTime:
print customer, joinTime[customer]
Catherine 1432137144.7
Steven 1432137137.77
Ugly but may be useful to someone:
class MyDefaultDict(defaultdict):
def __init__(self, func):
super(self.__class__, self).__init__(self._func)
self.func = func
def _func(self):
return self.func(self.cur_key)
def __getitem__(self, key):
self.cur_key = key
return super().__getitem__(self.cur_key)

How to call a function from another class in python

class A:
def add_f(self,a,b):
return a+b
class B:
def sum_f(self,a,b,c):
return A.add_f(a,b) + c
B1= B()
print B1.sum_f(1,2,3)
Traceback (most recent call last):
File "C:/Users/wxu/Documents/test.py", line 10, in <module>
print B1.sum_f(1,2,3)
File "C:/Users/wxu/Documents/test.py", line 7, in sum_f
return A.add_f(a,b) + c
TypeError: unbound method add_f() must be called with A instance as first argument (got int instance instead)
When I don't have the self for sum_f(self,a,b,c), it gave me this error:
Traceback (most recent call last):
File "C:/Users/wxu/Documents/test.py", line 10, in <module>
print test1.sum_f(1,2,3)
TypeError: sum_f() takes exactly 3 arguments (4 given)
Why is that?
And how to call function add_f from class A in class B? Thank you for your help.
There was a few things.
When you define class methods, they must have self as the first parameter.
The part where you had an error is where you tried to call B as a variable. B is a class, and you must call it like any other class. This also applies when you are calling A() in class B.
Revised code:
class A:
def add_f(self, a, b):
return a + b
class B:
def sum_f(self, a, b, c):
return A().add_f(a, b) + c
print B().sum_f(1, 2, 3)
Update:
Thanks for taking my advice but you're still missing something. In class B you call a method from class A, but you need parentheses for that too! In class B, call class A as such:
A().add_f(a, b)
Simple fixes, you just need to open and close the brackets on your class declarations, so class B becomes class B() (Same thing applied to class A). Also, you need to add some kind of variable such as self as the first argument of each method declared within a class. You never need to pass anything as this first argument to fill self, as python will do it for you.
As for your A.add_f issue, you just need to do as you did with B, and instantiate it an A object to something like A1, and then call the method of A1.

Attempting to create a read-only property attribute - getter returns initialized value, direct access returns changed value

Although I may be very confused as to what the property() function does, I'm trying to use it to create an attribute which is read-only. Ideally, I'd like to be able to refer to the attribute directly but not be allowed to assign to it. When experimenting, I got this very curious behavior:
>>> class Boo(object):
... def __init__(self, x):
... self.__x = x
... def getx(self):
... return self.__x
... x = property(getx)
...
>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.__x
2
I'd like to add that when I used x and _x as the attribute names, reassigning the attribute caused the getter to return the changed value, i.e. both b.getx() and b.x/b._x gave me 2.
I realize that I'm using x as the property name, though, but when I tried the following I got an AttributeError in my __init__():
>>> class Boo(object):
... def __init__(self, x):
... self.__x = x
... def getx(self):
... return self.__x
... __x = property(getx)
...
>>> b = Boo(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
AttributeError: can't set attribute
The problem here has nothing to do with property, but with double-underscore attributes, which are subject to what's called "Private name mangling".
>>> b = Boo(1)
>>> '__x' in dir(b)
False
>>> '_Boo__x' in dir(b)
True
So, when you do this:
>>> b.__x = 2
You're not changing the value of the attribute the getx function is looking at, you're creating a new attribute.
If you just use a name for the attribute that doesn't start with two underscores—such as _x—everything works as you intended.
As a general rule, use a single underscore for "advisory private"—as in, "users of this object probably shouldn't care about this value", and a double underscore only when you actually need mangling (because of complex inheritance issues that rarely come up).
What if you want "real private", like C++ or Java? You can't have it. If you hide or protect the attribute well enough, someone will just monkeypatch the getx method or the x property. So, Python doesn't give a way to hide attributes.
Your problem is that using double underscore attribute names mangles the name. So when you are dealing with __x inside of your class definition, outside of the class it actually looks like _Boo__x. That is,
_ + (class name) + (double underscore attribute name)
To demonstrate,
>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.x # NOTE: same as calling getx
1
>>> b.__x # why didn't x return 2 if we changed it?
2
>>> b._Boo__x # because it's actually saved in this attribute
1
>>> b._Boo__x = 3 # setting it here then works
>>> b.x
3
>>> b.getx()
3
Really just wanted to comment (rather than answer) your question. I think you will find the following informative:
>>> b = Boo(1)
>>> print b.__dict__
{'_Boo__X': 1}
>>> b.__x = 2
>>> print b.__dict__
{'__x': 2, '_Boo__X': 1}
Might provide a hint as to the behavior (which I do not understand sufficiently well to explain).
It's because you don't have a setx function defined.
#!/usr/bin/python
class Boo(object):
def __init__(self, initialize_x):
self.x = initialize_x
def getx(self):
print
print '\t\tgetx: returning the value of x', self.__x
print
return self.__x
def setx(self, new_x):
print
print '\t\tsetx: setting x to new value',new_x
print
self.__x = new_x
x = property(getx, setx)
print '1 ########'
print
print '\tinitializing Boo object with a default x of 20'
print
o = Boo(20)
print '\treading the value of x through property o.x'
t = o.x
print
print '2 ########'
print
print '\tsetting x\'s value through the property o.x'
o.x = 100
print
print '3 ########'
print
print '\treading the value of x through the property o.x'
t = o.x
When run produces:
1 ########
initializing Boo object with a default x of 20
setx: setting x to new value 20
reading the value of x through property o.x
getx: returning the value of x 20
2 ########
setting x's value through the property o.x
setx: setting x to new value 100
3 ########
reading the value of x through the property o.x
getx: returning the value of x 100
I use property as decorator. It convenient for calculating data.
May be for make read-only attribute better use magic function as __set__ and __get__?

difference between F(x) and F x in Python

In Python it is possible to call either del x or del (x) . I know how to define a function called F(x) , but I do not know how to define a function that cal be called like del, without a tuple as parameters.
What is the difference between F x and F(x), and how can I define a function that can be called without parenthesis ?
>>> a = 10
>>> a
10
>>> del a <------------ can be called without parenthesis
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> a = 1
>>> del (a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> def f(x): 1
...
>>> f (10)
>>> print f (10)
None
>>> def f(x): return 1
...
>>> print f (10)
1
>>> f 1 <------ cannot be called so
File "<stdin>", line 1
f 1
^
SyntaxError: invalid syntax
>>>
The main reason is that del is actually a statement and therefore has special behavior in Python. Therefore you cannot actually define these (and this behavior) yourself* - it is a built-in part of the language for a set of reserved keywords.
**I guess you could potentially edit the source of Python itself and build your own in, but I don't think that is what you're after :)*

Categories

Resources