What is assigned to `variable`, in `with expression as variable`? - python

From Learning Python:
The basic format of the with statement looks like this, with an
optional part in square brackets here:
with expression [as variable]:
with-block
The expression here is assumed to return an object that supports the
context management protocol (more on this protocol in a moment).
This object may also return a value that will be assigned to the name variable if the optional as clause is present.
Note that the variable is not necessarily assigned the result of
the expression; the result of the expression is the object that
supports the context protocol, and the variable may be assigned
something else intended to be used inside the statement.
expression is evaluated to a context manager object.
What is assigned to variable? The quote only says that it is not a context manager object.
Does the assignment to variable call some method of a context manager class to produce the actual value assigned to variable?
Thanks.

Whatever is returned from __enter__. From the documentation on the __enter__ method of context managers:
contextmanager.__enter__()
Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to the identifier in the as clause of with statements using this context manager.
(Emphasis mine)
The result of calling __enter__ could very well be a context manager, nothing in the specification forbids this. It could of course be another object related to the runtime context, as the docs state.
Objects that return themselves from __enter__ can be used again and again as context managers. file objects, for example:
with open('test_file') as f1: # file.__enter__ returns self
with f1 as f2: # use it again, get __self__ back
print("Super context managing")
with f2 as f3, f1 as f4: # getting weird.
print("This can go on since f1.__enter__ returns f1")
print("f1.__exit__ has been called here, though :)")
print("f1 closed: {}".format(f1.closed))
Not that the previous made much sense but just to make the point clear.

Your object can function as a context manager if it provides both __enter__ and __exit__. The object returned by __enter__ is bound to the object you specify in the as part of the with statement:
In [1]: class Foo:
...: def __enter__(self):
...: return 'hello'
...: def __exit__(self, *args):
...: pass
...:
In [2]: with Foo() as a:
...: print(a)
...:
hello

Related

Are all methods are preceded by a dot(.) or just some of them. How do we define it needs a dot or not?

I was learning Python by using Python Crash Course and came upon this String and Method thing: It only said that the dot(.) after name in name.title() tells Python to make the title() method act on the variable name.
Not always, you can create a method dynamically:
from types import MethodType
def fn(x):
return x.var
class A:
def __init__(self):
self.var = 20
obj = A()
method_ = MethodType(fn, obj)
print(method_)
print(method_())
output :
<bound method fn of <__main__.A object at 0x000001C5E3F01FD0>>
20
A method is an instance of type MethodType and also it has an object bound to it, when method gets called, it's first parameter will always get filled with that object. Here fn() function's first parameter (x) will be filled with obj object.
The above answer is precise but i wanted to add to it.
Actually methods are functions that take objects as arguments and then return values based on that and as python is an Object Oriented Language therefore everything in python is an object.
When you call name.title():
then, python search for the title() method for the name object.And as all methods are designated to take the object as an argument:
`def title(self):
...+
`
This is what a method definition look like inside a class and the self argument here stands for the object calling the method.
And we do not have to specify it explicitly it is recognised by the python interpreter.
As in your case: name.title() the object calling the method title() is the name variable therefore here self is assigned the value of name that is the function call name.title() is equivalent to title(name) but the former is the correct syntax of calling the method whereas the latter one is for comprehesion purpose.
If you run title(name) it surely gonna raise an error.
But, as the title() method belongs to the str class you can always call str.title(name).
Hope i didn't confuse you instead of making it clearer...Happy coding..:)

When does __getattr__ get triggered?

I have a class as follows:
class Lz:
def __init__(self, b):
self.b = b
def __getattr__(self, item):
return self.b.__getattribute__(item)
And I create an instance and print :
a = Lz('abc')
print(a)
Result is: abc
I have set a breakpoint at line return self.b.__getattribute__(item), item show __str__
I don't know why it calls __getattr__, and item is __str__ when I access the instance.
print calls __str__ (see this question for details), but as Lz does not have a __str__ method, a lookup for an attribute named '__str__' takes place using __getattr__.
So if you add a __str__ method, __getattr__ should not be called anymore when printing objects of the Lz class.
print(obj) invokes str(obj) (to get a printable representation), which in turn tries to invokes obj.__str__() (and fallback to something else if this fails, but that's not the point here).
You defined Lz as an old-style class, so it's doesn't by default have a __str__ method (new-style classes inherit this from object), but you defined a __getattr__() method, so this is what gets invoked in the end (__getattr__() is the last thing the attribute lookup will invoke when everything else has failed).
NB: in case you don't already know, since everything in Python is an object - include classes, functions, methods etc - Python doesn't make difference between "data" attributes and "method" attributes - those are all attributes, period.
NB2: directly accessing __magic__ names is considered bad practice. Those names are implementation support for operators or operator-like generic functions (ie len(), type etc), and you are supposed to use the operator or generic function instead. IOW, this:
return self.b.__getattribute__(item)
should be written as
return getattr(self.b, item)
(getattr() is the generic function version of the "dot" attribute lookup operator (.))

Is het possible when inheriting from str or unicode in python to keep class instance after assignment?

I think the answer will be no, but if there is a chance, it would make my life a lot easier. Consider the following code:
class SchemaString(unicode):
_schema = dict()
def validate(self):
errors = []
# some validation function using class propertie _schema, not important
return sorted(list(set(errors)))
s = SchemaString("Hello")
e0 = s.validate()
What I would like is that the following still works:
s = "World"
e1 = s.validate()
>> AttributeError: 'str' object has no attribute 'validate'
Thus my question is if it is possible that after new assignment still same object is used, such that I can still use the method 'validate'. In other words, is there some internal assigning function used when using '=' in python and can this assignment function be overwritten?
You can't override "top level" assignment. But if you can have a root object and work with variables inside a namespace object, then yes, you only have to write the __setattr__ method for that object. It can be a rather simple class, and you can make an instance that is a single letter, so it does not cause negative interference on reading your code - on the contrary, it is easy to perceive that something special is going on:
class NS(object):
def __setattr__(self, name, value):
if name not in self.__dict__:
# Normal, first time assignment:
super(NS, self).__setattr__(name, value)
return
cls = type(self.__dict__[name])
instance = cls(value)
super(NS, self).__setattr__(name, instance)
n = NS()
And on the ineteractive prompt:
In [110]: class MyString(str): pass
...:
...: n.s = MyString()
...:
...: n.s = "bla"
...: type(n.s)
...:
Out[110]: __main__.MyString
Without resorting to a container object, no, there is no way to override assignments - the under-the-hood generated code for that is completely different - as the bytecode sets the variables directly either in the "global" dictionary for the current frame, or in fast-variable caches if the code is running inside a function - meaning that not even the locals dictionary is touched. That is, it is possible to think of an observer-pattern that takes care of changes in the global dictionary, and takes action when a global variable is changed in a module (if your application is running in a custom event loop, for example) - but that would be too hacky, and would not work for global variables anyway. The namespace object is orders of magnitude simpler.
That said, if you are using Python3, (which you should - and then for one thing - stop worrying about "unicode" or "str" for text) - you could also override the assignment operator in the body of a class: for that you have to create a custom metaclass with a __prepare__ method that would create a mapping object in which the logic you want would be on the __setitem__ method. In this case, Python code just calls the __setitem__ method on this dictionary-like object when it finds an assignment statement.

Why does hasattr execute the #property decorator code block

In Python when I call the hasattr on a #property decorator the hasattr function actually runs the #property code block.
E.g. a class:
class GooglePlusUser(object):
def __init__(self, master):
self.master = master
def get_user_id(self):
return self.master.google_plus_service.people().get(userId='me').execute()['id']
#property
def profile(self):
# this runs with hasattr
return self.master.google_plus_service.people().get(userId='me').execute()
Running the following code calls the profile property and actually makes the call:
#Check if the call is an attribute
if not hasattr(google_plus_user, call):
self.response.out.write('Unknown call')
return
Why? How can I solve this without making the api call?
hasattr() works by actually retrieving the attribute; if an exception is thrown hasattr() returns False. That's because that is the only reliable way of knowing if an attribute exists, since there are so many dynamic ways to inject attributes on Python objects (__getattr__, __getattribute__, property objects, meta classes, etc.).
From the hasattr() documentation:
This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.
If you don't want a property to be invoked when doing this, then don't use hasattr. Use vars() (which returns the instance dictionary) or dir() (which gives you a list of names on the class as well). This won't let you discover dynamic attributes handled by __getattr__ or __getattribute__ hooks however.
hasattr is basically implemented like this (except in C):
def hasattr(obj, attrname):
try:
getattr(obj, attname)
except AttributeError:
return False
return True
So in true "easier to ask for forgiveness than permission" (EAFP) fashion, to find out if an object has a given attribute, Python simply tries to get the attribute, and converts failure to a return value of False. Since it's really getting the attribute in the success case, hasattr() can trigger code for property and other descriptors.
To check for an attribute without triggering descriptors, you can write your own hasattr that traverses the object's method resolution order and checks to see whether the name is in each class's __dict__ (or __slots__). Since this isn't attribute access, it won't trigger properties.
Conveniently, Python already has a way to walk the method resolution order and gather the names of attributes from an instance's classes: dir(). A simple way to write such a method, then, would be:
# gingerly test whether an attribute exists, avoiding triggering descriptor code
def gentle_hasattr(obj, name):
return name in dir(obj) or hasattr(obj, name)
Note that we fall back to using hasattr() if we can't find the desired name in dir(), because dir() won't find dynamic attributes (i.e., where __getattr__ is overridden). Code for these will still be triggered, of course, so if you don't care that you don't find them, you could omit the or clause.
On balance, this is wasteful, since it gets all relevant attribute names when we're interested only in whether a specific one exists, but it'll do in a pinch. I'm not even sure that doing the loop yourself rather than calling dir() would be faster on average, since it'll be in Python rather than in C.
Making the variable as a class variable and then calling hasattr on it did the trick for me.
if not hasattr(google_plus_user, GooglePlusUser.call):
self.response.out.write('Unknown call')
return

Object becomes None when using a context manager

Why doesn`t this work:
class X:
var1 = 1
def __enter__(self): pass
def __exit__(self, type, value, traceback): pass
with X() as z:
print z.var1
I get:
print z.var1
AttributeError: 'NoneType' object has no attribute 'var1'
Change the definition of X to
class X(object):
var1 = 1
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
pass
with assigns the return value of the __enter__() method to the name after as. Your __enter__() returned None, which was assigned to z.
I also changed the class to a new-style class (which is not critical to make it work).
See the docs for context managers:
__enter__( ) Enter the runtime context and return either this object or
another object related to the runtime
context. The value returned by this
method is bound to the identifier in
the as clause of with statements using
this context manager. An example of a
context manager that returns itself is
a file object. File objects return
themselves from __enter__() to allow
open() to be used as the context
expression in a with statement.
An example of a context manager that
returns a related object is the one
returned by
decimal.Context.get_manager(). These
managers set the active decimal
context to a copy of the original
decimal context and then return the
copy. This allows changes to be made
to the current decimal context in the
body of the with statement without
affecting code outside the with
statement.
Your __enter__ method doesn't return anything, which is the same as returning None.
The function you've defined between 'with' and 'as' must have and only have one return value. 'with' will pass the value to its built-in method __enter__().
A class-type object in Python will not return any value if not defined when you called it.
Similarly, if you called a class-type object with its method that returns nothing, it will also throw out exception.
You can't write like this:
with open('file.txt').readlines() as lines:
This generated two return values, and it's even not allowed to pass to one variable in Python.
But this is good to use:
with open('file.txt') as f:
lines = f.readlines()

Categories

Resources