Why is the __dict__ attribute of this function an empty dictionary? - python

I was testing out the pre-defined dict attribute on a function, and I got a result I didn't expect to get. Consider the following code:
>>> def func():
x = 7
a = 0
print(func.__dict__)
>>> func()
{}
After all, I did define two variables in the namespace of the function. So why aren't these names appearing in the dict attribute of the function?

A function's __dict__ holds attributes, not local variables. Local variables are specific to each execution of a function, not to the function itself, so you can't get local variable values by inspecting a function.
If you assigned an attribute:
func.blah = 3
that would show up in the __dict__.
(A __dict__ doesn't hold all attributes - there are other ways to create attributes, which is why __dict__ itself doesn't show up in the __dict__, for example.)

The __dict__ attribute of a function object stores attributes assigned to the function object.
>>> def foo():
... a = 2
... return a
...
>>> foo.bar = 12
>>> foo.__dict__
{'bar': 12}
Attributes of the function object are not related to the local variables that exist during the function call. The former is unique (there is one __dict__ per function object) the latter are not (a function may be called multiple times with separate local variables).
>>> def nfoo(n: int):
... print(f'level {n} before:', locals())
... if n > 0:
... nfoo(n - 1)
... print(f'level {n} after: ', locals())
...
>>> nfoo(2)
level 2 before: {'n': 2}
level 1 before: {'n': 1}
level 0 before: {'n': 0}
level 0 after: {'n': 0}
level 1 after: {'n': 1}
level 2 after: {'n': 2}
Note how the locals of each level exist at the same time but hold separate values for the same name.

__dict__ is there to support function attributes, it's not a namespace nor a symbol table for the function body scope. You can read PEP-232 for further information regarding function attributes.

Related

Deleting main object by deleting reference

I am working with large nested dictionaries, and am trying to delete nested subdictionaries. I am wondering why the following behavior occurs.
When I set a reference to dictionary d (called ref), then I change ref and print d, it shows an updated version of d with the third element added.
input:
d={"a":1,"b":2}
ref=d
ref["c"]=3
print(d)
output:
{'a': 1, 'b': 2, 'c': 3}
Given this behavior, I was expecting to be able to delete the dictionary by delete
input:
d={"a":1,"b":2}
ref=d
del ref
print(d)
output:
{'a': 1, 'b': 2}
I am wondering if there is a way to delete the original object when I delete the reference (meaning that the output of the second program would be an error because d was deleted.
del doesn't actually handle any de-allocation of memory, it merely unbinds a value from a name, and then decrements the reference count of that object by one. There is no way to systematically unbind all names from an object given a single reference.
An object is not garbage collected until some point after the reference count drops to 0. You can see an object's reference count by using the sys.getrefcount method (which is typically one higher than it actually is because of the temporary reference within the method itself).
We can demonstrate del in practice using this method and the __del__ method (which is called only when the reference count for the object is decremented to 0):
>>> # print something when refcount == 0 and object is about to be collected
>>> class Deleted:
... def __del__(self):
... print("actually deleted")
...
>>> a = Deleted()
>>> # just a
>>> sys.getrefcount(a) - 1
1
>>> b = a
>>> # a and b
>>> sys.getrefcount(a) - 1
2
>>> del a
>>> # now it's just b
>>> sys.getrefcount(b) - 1
1
>>> del b
actually deleted
If you're curious to read more about how all of this works internally, check out the C API documentation on the internal calls for reference counting, and check out the gc module, which is the high level python interface for introspecting the garbage collection sub-system.
Given your specific problem, since you are working with dictionaries which are mutable types, you could just clear the dictionary:
>>> a = {"a": 1}
>>> b = a
>>> # will clear the dict that both a and b are referencing
>>> b.clear()
>>> a
{}
Alternatively you can use the equivalent range syntax to clear the dictionary del a[:].
The del statement behaves differently depending on what is being deleted. Paraphrasing slightly:
Deletion of a name removes the binding of that name from the local or global namespace
That is the second case presented. You've got two references to the same object. The name ref has been deleted, but the name d still exists and points to the same object is always did.
However, attributes, subscriptions, and slicings have different behaviour:
Deletion of attribute references, subscriptions and slicings is passed to the primary object involved
That is more like the first case - deleting an element from either name will be reflected in the other:
input:
d = {"a":1, "b":2}
ref = d
del ref["a"]
print(d)
output:
{'b': 2}
So, wrapping the references inside a dictionary (or other container), will allow deletion by any reference.

Appending an attribute to a function in python

I am not sure exactly what this means or how it is used, but i came across this functionality when it comes to a function. I am able to dynamically add attributes to a function. Can someone help me understand what is the use of this and how does it happen? I mean its a function name after all, how can we add attributes to it dynamically?
def impossible():
print "Adding a bool"
impossible.enabled = True
# i was able to just do this. And this is a function, how does this add up?
print(impossible.enabled) # No attribute error. o/p --> True
in python functions are objects, they have all sorts of inner attributes and they can also have the __dict__ attribute, and like any other object anything in __dict__ is a variable of that object.you can see it if you print it before and after impossible.enabled
def impossible():
print "Adding a bool"
print(impossible.__dict__) # {}
impossible.enabled = True
print(impossible.__dict__) # {'enabled': True}
this doesn't change the function in any way since most of the time __dict__ is empty and holds no special value in a function.
in fact its a bit useful since you could use this in a function to store static variables without cluttering your global names
The name impossible points to an object that represents the function. You're setting a property on this object (which python allows you to do on all objects by default):
>>> def foo():
... print("test")
...
>>> dir(foo)
['__call__', ..., 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> foo.a = 'test'
>>> dir(foo)
['__call__', ..., 'a', ...]
Since this object implements __call__, it can be called as a function - you can implement your own class that defines __call__ and it'll also be allowed to be called as a function:
>>> class B:
... def __call__(self, **kw):
... print(kw)
...
>>> b = B()
>>> b()
{}
>>> b(foo='bar')
{'foo': 'bar'}
If you create a new function, you can see that you haven't done anything to the definition of a function (its class), just the object that represents the original function:
>>> def bar():
... print("ost")
...
>>> dir(bar)
['__call__', ..., 'func_closure', ...]
This new object does not have a property named a, since you haven't set one on the object bar points to.
You can, for example, use a function attribute to implement a simple counter, like so:
def count():
try:
count.counter += 1
except AttributeError:
count.counter = 1
return count.counter
Repeatedly calling "count()" yields: 1, 2, 3, ...

Change in state and scope in python

I am working with the requests python library and flask.
I'd like to open a page "example.com" and then follow a link to open "example.com/linked_page.html"
My plan is to use request sessions like this (simplified):
def main():
s = requests.Session()
d = {'session':s, 'e': None}
html = get_base_page(d)
def get_base_page(dict):
s = dict['session']
html = s.get("example.com")
return html
Since the state of the session is changed within a function will that affect the value of "s" within the 'main' (global?) scope. Also is there a good discussion of this anywhere?
The get_base_page function has its own namespace, so there would be no interference between the two s variables.
You can access global variables from the get_base_page function.
But if you want to assign a value to them you need to declare them as global.
Otherwise they would be created in the function's local namespace, as it does occur in your case.
See this article for a good tutorial about namespaces and scope (LEGB rule)
Reading your question again, I think the point we need to clarify is how the s object is stored as the value of the key named session in the d dictionary.
Now fire up python and do the following (>>> is the python prompt):
>>> s = 42
>>> d = {'session': s, 'key2': 8}
>>> d
{'key2': 8, 'session': 42}
>>> s = 3
>>> d
{'key2': 8, 'session': 42}
When you say s = 3, you are telling python to make the s name point to/indicate the value 3. You are not overwriting the value itself.
Now let's do the same thing with a generic object of a class we define:
>>> class A(object):
... def __init__(self, n):
... self.n = n
...
>>> s = A(42)
>>> d = {'session': s, 'key2': 8}
>>> d['session'].n
42
>>> s.n = 30
>>> d['session'].n
30
See the difference? The s name and the session key point to/indicate the same object instance. So when you change its attributes (reaching them through either name), the change will affect both.
Please refer to this article for immutable/mutable objects.
Practice with all python types and see what happens. Have fun!
The two s variables are independent of each other.... unless you use global s.

What is the dfifference between instance dict and class dict

I was reading the python descriptors and there was one line there
Python first looks for the member in the instance dictionary. If it's
not found, it looks for it in the class dictionary.
I am really confused what is instance dict and what is class dictionary
Can anyone please explain me with code what is that
I was thinking of them as same
An instance dict holds a reference to all objects and values assigned to the instance, and the class level dict holds all references at the class namespace.
Take the following example:
>>> class A(object):
... def foo(self, bar):
... self.zoo = bar
...
>>> i = A()
>>> i.__dict__ # instance dict is empty
{}
>>> i.foo('hello') # assign a value to an instance
>>> i.__dict__
{'zoo': 'hello'} # this is the instance level dict
>>> i.z = {'another':'dict'}
>>> i.__dict__
{'z': {'another': 'dict'}, 'zoo': 'hello'} # all at instance level
>>> A.__dict__.keys() # at the CLASS level, only holds items in the class's namespace
['__dict__', '__module__', 'foo', '__weakref__', '__doc__']
I think, you can understand with this example.
class Demo(object):
class_dict = {} # Class dict, common for all instances
def __init__(self, d):
self.instance_dict = d # Instance dict, different for each instance
And it's always possible to add instance attribute on the fly like this: -
demo = Demo({1: "demo"})
demo.new_dict = {} # A new instance dictionary defined just for this instance
demo2 = Demo({2: "demo2"}) # This instance only has one instance dictionary defined in `init` method
So, in the above example, demo instance has now 2 instance dictionary - one added outside the class, and one that is added to each instance in __init__ method. Whereas, demo2 instance has just 1 instance dictionary, the one added in __init__ method.
Apart from that, both the instances have a common dictionary - the class dictionary.
Those dicts are the internal way of representing the object or class-wide namespaces.
Suppose we have a class:
class C(object):
def f(self):
print "Hello!"
c = C()
At this point, f is a method defined in the class dict (f in C.__dict__, and C.f is an unbound method in terms of Python 2.7).
c.f() will make the following steps:
look for f in c.__dict__ and fail
look for f in C.__dict__ and succeed
call C.f(c)
Now, let's do a trick:
def f_french():
print "Bonjour!"
c.f = f_french
We've just modified the object's own dict. That means, c.f() will now print Bounjour!. This does not affect the original class behaviour, so that other C's instances will still speak English.
Class dict is shared among all the instances (objects) of the class, while each instance (object) has its own separate copy of instance dict.
You can define attributes separately on a per instance basis rather than for the whole class
For eg.
class A(object):
an_attr = 0
a1 = A()
a2 = A()
a1.another_attr = 1
Now a2 will not have another_attr. That is part of the instance dict rather than the class dict.
Rohit Jain has the simplest python code to explain this quickly. However, understanding the same ideas in Java can be useful, and there is much more information about class and instance variables here

Accessing static properties in Python

I am relatively new to Python and was hoping someone could explain the following to me:
class MyClass:
Property1 = 1
Property2 = 2
print MyClass.Property1 # 1
mc = MyClass()
print mc.Property1 # 1
Why can I access Property1 both statically and through a MyClass instance?
The code
class MyClass:
Property1 = 1
creates a class MyClass which has a dict:
>>> MyClass.__dict__
{'Property1': 1, '__doc__': None, '__module__': '__main__'}
Notice the key-value pair 'Property1': 1.
When you say MyClass.Property1, Python looks in the dict MyClass.__dict__ for the key Property1 and if it finds it, returns the associated value 1.
>>> MyClass.Property1
1
When you create an instance of the class,
>>> mc = MyClass()
a dict for the instance is also created:
>>> mc.__dict__
{}
Notice this dict is empty. When you say mc.Property1, Python first looks in mc.__dict__ for the 'Property1' key. Since it does not find it there, it looks in the dict of mc's class, that is, MyClass.__dict__.
>>> mc.Property1
1
Note that there is much more to the story of Python attribute access. (I haven't mentioned the important rules concerning descriptors, for instance.) But the above tells you the rule for most common cases of attribute access.

Categories