Unexpected SyntaxError with decorators - python

Here the goal is to call a global function as a decorator.
#coding: utf-8
def Test(method_to_decorate):
print 'Decorator'
def wrapper(self):
return method_to_decorate(self)
return wrapper
class Smth(object):
def Test(self):
print 'NOT a decorator!'
a=globals()['Test']
##globals()['Test'] --> SyntaxError
#a # works fine
def Fun(self):
print "Smth.Fun()"
l = Smth()
l.Fun()
Using the uncommented approach works fine while #globals()['Test'] gives SyntaxError. Why? I'm quite sure Test exists in globals().
The thing after # must be a function accepting another function as one of its arguments, am I right? globals()['Test'] is such a function, isn't it? Then, in my opinion, #globals()['Test'] must be correct (logically correct).
Edit
This works as expected:
def Ex(*args):
print 'Ex({})'.format(args)
globals()['Ex']('hello') # just calling a function
While a decorator is used, a function is called. We can call functions as above but can't use a decorator like this. Maybe this is some sort of a Python bug or logic misorganisation?

It's a SyntaxError because Python's grammar explicitly does not allow that; the tokens on the right of # are not an expression. 1 Instead, valid syntax is:
decorator: '#' dotted_name [ '(' [arglist] ')' ] NEWLINE
(A phrase enclosed in square brackets ([ ]) means zero or one occurrences (in other words, the enclosed phrase is optional).) 2
Following from the above, you can get this to work by converting globals()['Test'] to a dotted name. The following examples should work:
g = globals()
#g.get('Test')
def Fun(self):
pass
x = globals()['Test']
#x
def Fun(self):
pass
Or, as you noticed, you can just skip the syntactic sugar and decorate manually, which is probably the least bad option.

Because the Python parser isn't designed to recognize an index access as part of decorator syntax. Consider writing a separate function that accesses globals() instead.

Related

What is the difference between Pass and None in Python

I would personally like to know the semantic difference between using Pass and None. I could not able to find any difference in execution.
PS: I could not able to find any similar questions in SO. If you find one, please point it out.
Thanks!
pass is a statement. As such it can be used everywhere a statement can be used to do nothing.
None is an atom and as such an expression in its simplest form. It is also a keyword and a constant value for “nothing” (the only instance of the NoneType). Since it is an expression, it is valid in every place an expression is expected.
Usually, pass is used to signify an empty function body as in the following example:
def foo():
pass
This function does nothing since its only statement is the no-operation statement pass.
Since an expression is also a valid function body, you could also write this using None:
def foo():
None
While the function will behave identically, it is a bit different since the expression (while constant) will still be evaluated (although immediately discarded).
In simple terms, None is a value that you can assign to a variable that signifies emptiness. It can be useful as a default state:
a = None
def f():
a = 5
f()
pass is a statement that is like a nop. It can be useful when you are defining function stubs, for instance:
def f():
pass
In C-like languages, you would be able to define empty functions by simply putting nothing between the braces void f() { }, but since Python uses indentation instead of braces to define blocks, you must put something in the body, and pass is the idiomatic thing to put there.
That's absolute difference between pass and None
The pass (without upper case P):
Because python be the indent base language, so if you define a new method, you should have some code after that.
def method_a():
some_thing = 1 # Have to do some thing
If not, an exception should be raised so you could use the pass keyword for hacks this problem.
def method_a():
pass # Do nothing
The None:
So very different, the None keyword has a little bit same to the null keywords from another language like Java or C. That may be the empty data or not assign data like that.
[] == None
null == None
() == None
...

python imported module dot notation as function parameter input

New to python programming here. I need some help understand why this does not work:
import x.y # x is a package with __init__.py
def func1(x.y.z): # I get a syntax error here
It works when I do this:
import x.y
a = x.y.z
def func1(a):
I've search the web and can't find anything that would answer this somewhat directly.
Thanks.
With def you define new functions which accept some possibly unknown(!) arguments.
So, def sin(x): means "define a function called sin that accepts one argument". Note that this code means that x can be absolutely anything, the function definition doesn't (and cannot) apply any restrictions on its type, value, size, etc.
When you do
a = "hello"
def test(a):
pass
The a in the function definition is merely an argument that doesn't have any relation to any other a you use in your code! You could've called it x, pi, z or whatever as the name doesn't really matter (code readability aside).
When you try to write
def test(x.y.z):
pass
You get a syntax error as there exist restrictions on the variables' and arguments' names that don't allow you to call a variable any name you want. Why? Simply because otherwise you'll get a lot of uncertainty. For example, how to parse this:
# a poorly formatted number literal or a variable definition??
1234hello = "test"
# attempt to access a member of a class (or module) or a variable definition??
x.y.z = 5
# is "yay a variable's name or a poorly formatted string literal??
x = "yay - 5
# the same question as above
f' = df/dx
A function argument is a variable, so the very same restrictions are imposed on it as well.
BTW, take a look at the SO code highlighter going nuts trying to highlight the code above.

When is Python code between """ """ executed? [duplicate]

Let's say, I've got a function like this:
def myFunc():
# useful function to calculate stuff
This will produce an indentation error, unless I add pass:
def myFunc():
# useful function to calculate stuff
pass
However, if I replace a comment with docstring, no pass is necessary:
def myFunc():
"""useful function to calculate stuff"""
This seems like an odd feature as neither of these are used in the program, as far as I know. So, why does it behave like this?
A comment is outright ignored by the interpreter, so omitting a block after an indent is a syntax error. However, a docstring is a real Python object--at its most basic, a literal str. A lone expression is a valid block of code:
'This is a string. It is a valid (though pretty useless) line of Python code.'
In the case of docstrings in particular, there's also some additional functionality going on, such as being used to set the __doc__ attribute.
>>> def myFunc():
... '''MyDocString'''
...
>>> print(myFunc.__doc__)
MyDocString
Note that this also works for classes:
>>> class MyClass(object):
... '''MyClassDocString'''
...
>>> print(MyClass.__doc__)
MyClassDocString
A docstring isn't just a comment. It actually has meaning to the interpreter. In the case with a docstring, you could do myFunc.__doc__ and actually get your docstring back (In the other case with a pass, the result myFunc.__doc__ would be None).
In other words, you are actually adding some code to the function body to modify it's behavior (in some circumstances), so no pass is necessary.

How does this function call work? (Involves multiple pairs of brackets and decorators)

On this page of the official Python documentation about decorators and 'compound statements', this sample of code is given:
[Begin of sample]
#f1(arg)
#f2
def func(): pass
is equivalent to:
def func(): pass
func = f1(arg)(f2(func))
[End of sample]
However, I don't understand 'func = f1(arg)(f2(func))'. I've never seen a call like this before, and I have no idea of what it means. Is it multiple calls using different arguments, each pair of brackets containing one argument ('arg' in the first, 'f2(func)' in the second), or is it something else? I need to understand this in order to be able to study decorators. Also, does this work in Python 2.7? One of the sites I consulted on decorators was about Python 3.2.
What you need to know for this is that functions are first class objects - you can pass functions around just like ints or strs.
f1 returns a function, so what you've posted is similar to:
def func(): pass
f1_ret = f1(arg)
f2_ret = f2(func)
func = f1_ret(f2_ret)
The result of a Python function call is a value like any other. If you want, you can write func = f1(arg)(f2(func)) as
def func(): pass
x = f2(func)
y = f1(arg)
func = y(x)
A decorator that takes arguments must return a function that does the actual decorating. So #f1(arg) calls f1(arg). f1 returns a function, which can be called using the usual (...) notation: f1(arg)(...). What goes in the second set of parentheses? The function being decorated, which is f2(func) because of the second decorator. So put it all together and you get f1(arg)(f2(func)).
The piece you're missing, then, is that f1 returns a function and the second set of parentheses are calling the returned function.

Can I be warned when I used a generator function by accident

I was working with generator functions and private functions of a class. I am wondering
Why when yielding (which in my one case was by accident) in __someFunc that this function just appears not to be called from within __someGenerator. Also what is the terminology I want to use when referring to these aspects of the language?
Can the python interpreter warn of such instances?
Below is an example snippet of my scenario.
class someClass():
def __init__(self):
pass
#Copy and paste mistake where yield ended up in a regular function
def __someFunc(self):
print "hello"
#yield True #if yielding in this function it isn't called
def __someGenerator (self):
for i in range(0, 10):
self.__someFunc()
yield True
yield False
def someMethod(self):
func = self.__someGenerator()
while func.next():
print "next"
sc = someClass()
sc.someMethod()
I got burned on this and spent some time trying to figure out why a function just wasn't getting called. I finally discovered I was yielding in function I didn't want to in.
A "generator" isn't so much a language feature, as a name for functions that "yield." Yielding is pretty much always legal. There's not really any way for Python to know that you didn't "mean" to yield from some function.
This PEP http://www.python.org/dev/peps/pep-0255/ talks about generators, and may help you understand the background better.
I sympathize with your experience, but compilers can't figure out what you "meant for them to do", only what you actually told them to do.
I'll try to answer the first of your questions.
A regular function, when called like this:
val = func()
executes its inside statements until it ends or a return statement is reached. Then the return value of the function is assigned to val.
If a compiler recognizes the function to actually be a generator and not a regular function (it does that by looking for yield statements inside the function -- if there's at least one, it's a generator), the scenario when calling it the same way as above has different consequences. Upon calling func(), no code inside the function is executed, and a special <generator> value is assigned to val. Then, the first time you call val.next(), the actual statements of func are being executed until a yield or return is encountered, upon which the execution of the function stops, value yielded is returned and generator waits for another call to val.next().
That's why, in your example, function __someFunc didn't print "hello" -- its statements were not executed, because you haven't called self.__someFunc().next(), but only self.__someFunc().
Unfortunately, I'm pretty sure there's no built-in warning mechanism for programming errors like yours.
Python doesn't know whether you want to create a generator object for later iteration or call a function. But python isn't your only tool for seeing what's going on with your code. If you're using an editor or IDE that allows customized syntax highlighting, you can tell it to give the yield keyword a different color, or even a bright background, which will help you find your errors more quickly, at least. In vim, for example, you might do:
:syntax keyword Yield yield
:highlight yield ctermbg=yellow guibg=yellow ctermfg=blue guifg=blue
Those are horrendous colors, by the way. I recommend picking something better. Another option, if your editor or IDE won't cooperate, is to set up a custom rule in a code checker like pylint. An example from pylint's source tarball:
from pylint.interfaces import IRawChecker
from pylint.checkers import BaseChecker
class MyRawChecker(BaseChecker):
"""check for line continuations with '\' instead of using triple
quoted string or parenthesis
"""
__implements__ = IRawChecker
name = 'custom_raw'
msgs = {'W9901': ('use \\ for line continuation',
('Used when a \\ is used for a line continuation instead'
' of using triple quoted string or parenthesis.')),
}
options = ()
def process_module(self, stream):
"""process a module
the module's content is accessible via the stream object
"""
for (lineno, line) in enumerate(stream):
if line.rstrip().endswith('\\'):
self.add_message('W9901', line=lineno)
def register(linter):
"""required method to auto register this checker"""
linter.register_checker(MyRawChecker(linter))
The pylint manual is available here: http://www.logilab.org/card/pylint_manual
And vim's syntax documentation is here: http://www.vim.org/htmldoc/syntax.html
Because the return keyword is applicable in both generator functions and regular functions, there's nothing you could possibly check (as #Christopher mentions). The return keyword in a generator indicates that a StopIteration exception should be raised.
If you try to return with a value from within a generator (which doesn't make sense, since return just means "stop iteration"), the compiler will complain at compile-time -- this may catch some copy-and-paste mistakes:
>>> def foo():
... yield 12
... return 15
...
File "<stdin>", line 3
SyntaxError: 'return' with argument inside generator
I personally just advise against copy and paste programming. :-)
From the PEP:
Note that return means "I'm done, and have nothing interesting to
return", for both generator functions and non-generator functions.
We do this.
Generators have names with "generate" or "gen" in their name. It will have a yield statement in the body. Pretty easy to check visually, since no method is much over 20 lines of code.
Other methods don't have "gen" in their name.
Also, we do not every use __ (double underscore) names under any circumstances. 32,000 lines of code. Non __ names.
The "generator vs. non-generator" method function is entirely a design question. What did the programmer "intend" to happen. The compiler can't easily validate your intent, it can only validate what you actually typed.

Categories