I am reading the Python Cookbook 3rd Edition and came across the topic discussed in 2.6 "Searching and Replacing Case-Insensitive Text," where the authors discuss a nested function that is like below:
def matchcase(word):
def replace(m):
text = m.group()
if text.isupper():
return word.upper()
elif text.islower():
return word.lower()
elif text[0].isupper():
return word.capitalize()
else:
return word
return replace
If I have some text like below:
text = 'UPPER PYTHON, lower python, Mixed Python'
and I print the value of 'text' before and after, the substitution happens correctly:
x = matchcase('snake')
print("Original Text:",text)
print("After regsub:", re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE))
The last "print" command shows that the substitution correctly happens but I am not sure how this nested function "gets" the:
PYTHON, python, Python
as the word that needs to be substituted with:
SNAKE, snake, Snake
How does the inner function replace get its value 'm'?
When matchcase('snake') is called, word takes the value 'snake'.
Not clear on what the value of 'm' is.
Can any one help me understand this clearly, in this case?
Thanks.
When you pass a function as the second argument to re.sub, according to the documentation:
it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string.
The matchcase() function itself returns the replace() function, so when you do this:
re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE)
what happens is that matchcase('snake') returns replace, and then every non-overlapping occurrence of the pattern 'python' as a match object is passed to the replace function as the m argument. If this is confusing to you, don't worry; it is just generally confusing.
Here is an interactive session with a much simpler nested function that should make things clearer:
In [1]: def foo(outer_arg):
...: def bar(inner_arg):
...: print(outer_arg + inner_arg)
...: return bar
...:
In [2]: f = foo('hello')
In [3]: f('world')
helloworld
So f = foo('hello') is assigning a function that looks like the one below to a variable f:
def bar(inner_arg):
print('hello' + inner_arg)
f can then be called like this f('world'), which is like calling bar('world'). I hope that makes things clearer.
Related
Functions can be called in a couple ways:
say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)
The latter seems to pass a Positional, but apart from that I don't know how else they differ. Are there any differences that are important to know? What types of situations would you use one over the other?
#jjmerelo's answer covers the basics. This complementary answer, which aims at being somewhat exhaustive but hopefully not exhausting, covers traps, rare cases, and advice.
foo: valuea, valueb, ...
Surprisingly perhaps, this is not a call of a sub or method called foo.
Instead it's a statement that begins with a label, foo:.
The say: line in your question won't work in an ordinary program:
say: <a b c>; # Useless use of constant value a b c ...
The "Useless use" warning means the <a b c> doesn't get used in a useful way. The say: isn't doing anything with the list of values. It's just a label that doesn't do anything.
Presumably you are using something like the Perl 6 REPL. The REPL automatically says the last value in a line if it isn't otherwise used, thus making the line appear to work without a warning.
.a-method:
If a postfix method call using the form .a-method has no arguments other than the invocant (the argument to the left of the ., or the current topic if there isn't an explicit invocant) then you can just write it in the form:
42.say ;
You can optionally append a colon:
42.say: ;
There's no good reason to, but it's consistent with:
.a-method: arg2, arg3, ...
If you want to supply one or more arguments (other than the invocant) to a postfix .a-method call, then you have to pick one of two ways to introduce them.
One way is to write a colon immediately after the method name, before the argument(s). There must be no space between the method name and colon, and there must be space after the colon before the method argument(s).1
For example, the following uses a colon before the Numeric argument in the following method call:
say <abc 2 def ghi> .first: Numeric ; # 2
In the above line the method call expression (.first: Numeric) ends at the statement terminator (;). If there's an enclosing sub-expression such as an array subscript then the method call expression ends at the end of that sub-expression:
say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi
The argument list of a colon form method call is also brought to a close by a valid statement modifier like given:
say .first: Numeric given <abc 2 def ghi> ; # 2
a-sub arg1, arg2, ...
This is the corresponding form for subroutine calls. The only format differences are that the sub has no invocant or . before the sub name and you must omit the colon after the sub name.
.a-method( arg2, arg3, ... )
a-sub( arg1, arg2, ... )
The other common form used for both method and sub calls is to immediately follow the method or sub name with parens to delimit arguments. The opening paren must immediately follow, without any space between the routine name and (.
Here's parens used with the .first method:
say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3
This has the advantage that it's arguably prettier than the alternative of using outer parens:
say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3
If you want to put a sub call directly inside a double quoted string, you need to prefix the sub name with an & sigil and use the postfix parens form:
my #array = <abc 2 def ghi> ;
say "first number is &first(Numeric,#array)" ; # first number is 2
To put in a method call, you again have to use the postfix parens form, and you must also provide an explicit invocant (you can't just write "Some text .a-method()"):
my #array = <abc 2 def ghi> ;
say "first number is #array.first(Numeric)" ; # first number is 2
If there are no arguments (other than the invocant for a method call) you still need to use this form with empty parens if you want to interpolate a sub or method call in a string:
my #array = <abc 2 def ghi> ;
say "no method call #array[3].uc" ; # no method call ghi.uc
say "with method call #array[3].uc()" ; # with method call GHI
say "&rand"; # &rand
say "&rand()"; # 0.929123203371282
.a-method ( arrgh, arrgh, ... ) ;
This won't work.
Because the .a-method isn't followed by a colon, the method call is considered complete.
That means the next thing must be either an expression/statement ender like ;, or a postfix operator that will operate on the result of the method call, or an infix operator that will operate on the result and some following argument.
But ( arrgh, arrgh, ... ) is none of these. So you get a "Two terms in a row" compilation error.
.a-method:( arrgh, arrgh, ... ) ;
.a-method: ( arrgh, arrgh, ... ) ;
In general, DO NOT MIX use of a : with use of parens around arguments as part of a method call. There is no good reason to do so because it will either not work, or work only by accident, or work but very likely confuse readers.
Doing so without a space between the colon and opening paren yields a cryptic compilation error:
This type (QAST::WVal) does not support positional operations
Leaving a space appears to work -- but typically only by luck:
say .first: (Numeric) given <abc 2 def ghi> ; # 2
The (Numeric) is a single value in parens which yields Numeric so this line is the same as:
say .first: Numeric given <abc 2 def ghi> ; # 2
But if there are two or more arguments in parens, things will go awry. Use one of these forms:
say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1
which correctly yield the array index ("key") of the 2 element rather than:
say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil
which yields Nil because the .first method doesn't do anything useful with a single argument that's a list of the form (Numeric, :k).
Of course, you may occasionally want to pass a single argument that's a list of values in parens. But you can do so without using a colon. For the sake of clarity, it's my advice that you instead write this as:
invocant.a-method(( valuea, valueb, ... ));
a-sub ( arrgh1, arrgh2, ... ) ;
As just explained for method calls, this passes ONE argument to a-sub, namely the single list ( arrgh1, arrgh2, ... ) which will seldom be what the writer means.
Again, my advice is to instead write this as:
`a-sub( valuea, valueb, ... ) ;`
or:
`a-sub valuea, valueb, ... ;`
if you mean to pass multiple arguments, or if you wish to pass a list as a single argument, then:
`a-sub(( valuea, valueb, ... )) ;`
.a-method : arrgha, arrghb, ...
a-sub : arrgha, arrghb, ...
For the method form this will net you a "Confused" compilation error.
The same is true for the sub form if a-sub takes no arguments. If a-sub takes arguments you'll get a "Preceding context expects a term, but found infix : instead" compilation error.
.&a-sub
There's a call form which lets you call a routine declared as a sub -- but use the .method call syntax. The following feeds the "invocant" qux on the left of the dot as the first argument to a sub called a-sub:
qux.&a-sub
Use a : or parentheses as usual to pass additional arguments to a-sub:
sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42; # True
(In my original version of this section I wrote that one can not pass additional arguments. I had tested this and thought one could not. But I must have just gotten confused by something. #Enheh's comment led me to retest and discover that one can pass additional arguments just as with ordinary method calls. Thank you #Enheh. :))
a-method( invocant: arg2, arg3, ... )
a-method invocant: arg2, arg3, ...
Called "Indirect object notation" in the design docs, these formats are an undocumented and very rarely seen form of method call in which the call mimics the method declaration -- the method name comes first and then the invocant followed by a colon:
say first <abc 2 def ghi>: Numeric ; # 2
Note that say is a sub call because the next token, first, isn't followed by a colon. In contrast first is a method call because the token after it is followed by a colon.
Footnotes
1 All comments about spaces/spacing in this answer ignore unspacing.
As Raiph tells you above, say: is a label. So you didn't say anything (even though you thought you did) and -- outside use of the REPL -- the compiler will complain that your use of <a b c> was useless:
say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)»
However, you often can use a : notation instead of parentheses in method calls. Consider the four routine calls below (two subroutine calls then two method calls):
my #numbers = (33, 77, 49, 11, 34);
say map *.is-prime, #numbers ; # simplest subroutine call syntax
say map( *.is-prime, #numbers ); # same meaning, but delimiting args
say #numbers.map( *.is-prime ) ; # similar, but using .map *method*
say #numbers.map: *.is-prime ; # same, but using : instead of parens
These sentences will all return the same (False False False True False).
In general, as you see above with map, you can use () in method calls wherever you would use :, but the opposite is not true; : can be used only in method calls.
Use () if the arguments need to be delimited precisely, as Raiph comments below.
This answer focuses on the basics. See Raiph's answer for more exhaustive coverage of the precise details of routine call syntax. (As an important example, the meaning of these calls normally changes if there's any spaces between the routine name and the colon (:) or opening parenthesis (()).
I have a string function (and I am sure it is reversible, so no need to test this), could I call it in reverse to perform the opposite operation?
For example:
def sample(s):
return s[1:]+s[:1]
would put the first letter of a string on the end and return it.
'Output' would become 'utputO'.
When I want to get the opposite operation, could I use this same function?
'utputO' would return 'Output'.
Short answer: no.
Longer answer: I can think of 3, maybe 4 ways to approach what you want -- all of which depend on how are you allowed to change your functions (possibly restricting to a sub-set of Python or mini language), train them, or run them normally with the operands you are expecting to invert later.
So, method (1) - would probably not reach 100% determinism, and would require training with a lot of random examples for each function: use a machine learning approach. That is cool, because it is a hot topic, this would be almost a "machine learning hello world" to implement using one of the various frameworks existing for Python or even roll your own - just setup a neural network for string transformation, train it with a couple thousand (maybe just a few hundred) string transformations for each function you want to invert, and you should have the reverse function. I think this could be the best - at least the "least incorrect" approach - at least it will be the more generic one.
Method(2): Create a mini language for string transformation with reversible operands. Write your functions using this mini language. Introspect your functions and generate the reversed ones.
May look weird, but imagine a minimal stack language that could remove an item from a position in a string, and push it on the stack, pop an item to a position on the string, and maybe perform a couple more reversible primitives you might need (say upper/lower) -
OPSTACK = []
language = {
"push_op": (lambda s, pos: (OPSTACK.append(s[pos]), s[:pos] + s[pos + 1:])[1]),
"pop_op": (lambda s, pos: s[:pos] + OPSTACK.pop() + s[pos:]),
"push_end": (lambda s: (OPSTACK.append(s[-1]), s[:-1])[1]),
"pop_end": lambda s: s + OPSTACK.pop(),
"lower": lambda s: s.lower(),
"upper": lambda s: s.upper(),
# ...
}
# (or pip install extradict and use extradict.BijectiveDict to avoid having to write double entries)
reverse_mapping = {
"push_op": "pop_op",
"pop_op": "push_op",
"push_end": "pop_end",
"pop_end": "push_end",
"lower": "upper",
"upper": "lower"
}
def engine(text, function):
tokens = function.split()
while tokens:
operator = tokens.pop(0)
if operator.endswith("_op"):
operand = int(tokens.pop(0))
text = language[operator](text, operand)
else:
text = language[operator](text)
return text
def inverter(function):
inverted = []
tokens = function.split()
while tokens:
operator = tokens.pop(0)
inverted.insert(0, reverse_mapping[operator])
if operator.endswith("_op"):
operand = tokens.pop(0)
inverted.insert(1, operand)
return " ".join(inverted)
Example:
In [36]: sample = "push_op 0 pop_end"
In [37]: engine("Output", sample)
Out[37]: 'utputO'
In [38]: elpmas = inverter(sample)
In [39]: elpmas
Out[39]: 'push_end pop_op 0'
In [40]: engine("utputO", elpmas)
Out[40]: 'Output'
Method 3: If possible, it is easy to cache the input and output of each call, and just use that to operate in reverse - it could be done as a decorator in Python
from functools import wraps
def reverse_cache(func):
reverse_cache = {}
wraps(func)
def wrapper(input_text):
result = func(input_text)
reverse_cache[result] = input_text
return result
wrapper.reverse_cache = reverse_cache
return wrapper
Example:
In [3]: #reverse_cache
... def sample(s):
... return s[1:]+s[:1]
In [4]:
In [5]: sample("Output")
Out[5]: 'utputO'
In [6]: sample.reverse_cache["utputO"]
Out[6]: 'Output'
Method 4: If the string operations are limited to shuffling the string contents in a deterministic way, like in your example, (and maybe offsetting the character code values by a constant - but no other operations at all), it is possible to write a learner function without the use of neural-network programming: it would construct a string with one character of each (possibly with code-points in ascending order), pass it through the function, and note down the numeric order of the string that was output -
so, in your example, the reconstructed output order would be (1,2,3,4,5,0) - given that sequence, one just have to reorder the input for the inverted function according to those indexes - which is trivial in Python:
def order_map(func, length):
sample_text = "".join(chr(i) for i in range(32, 32 + length))
result = func(sample_text)
return [ord(char) - 32 for char in result]
def invert(func, text):
map_ = order_map(func, len(text))
reordered = sorted(zip(map_, text))
return "".join(item[1] for item in reordered)
Example:
In [47]: def sample(s):
....: return s[1:] + s[0]
....:
In [48]: sample("Output")
Out[48]: 'utputO'
In [49]: invert(sample, "uputO")
Out[49]: 'Ouput'
In [50]:
I am making a terminal emulator in Python 3. The commands are being stored in functions, like:
def rd(os_vartmp, os_vartmp2):
if os_vartmp == None:
print('rd [path] [-S]')
print('Delete a folder')
else:
if os.path.isfile(os_vartmp) == True:
if os_vartmp2 == '-S': print('a')
else:
print(ERR5)
a = input('Command: ')
The terminal works like this:
Asks user for input
Splits the input
Uses the first part of input to search a function in locals
If there is one, uses the rest part of input as argument
Calls the function
The thing here is, when i call the function 'rd' with, for example, 'rd "boot.py" -S' it works just fine. But if i need to call it like this: rd "boot.py", it throws me a error about 1 argument given when 2 are required. Is there a fix for that?
You can make an argument optional by assigning a value in the method definition. For example:
def Add(x=0, y=0):
return x+y
If you input only one value, y will default to 0. If I wanted to give y a value but have x fall back on it's default value I could do Add(y=10). I hope this helped!
Have you tried this?
def rd(os_vartmp, os_vartmp2="-S"):
Instead of trying to get null value, which would require rd("boot.py",null), you can ser default value and then you can do rd("boot.py").
Hope it works.
Consider this simple function:
>>> def foo(a):
... x = 2
... term = x + a
... return term
...
>>> foo(2)
4
>>>
Now along with the result I would like to have the code of the term with its assigned values like this:
# example
>>> foo(2)
4, 'term = 2 + 2'
How can I accomplish this?
I wrote the TeXcalc module to do something similar. It takes calculations and both formats them nicely for LaTeX and calculates the results.
It uses the ast module but also eval and exec, so it shouldn't be used with untrusted input.
The formatting is done by a custom object derived from the ast.NodeVisitor class. This was the hardest part to get right. It should at least give you an idea how to do this.
The use of the command "return" has always been bothering me since I started learning Python about a month ago(completely no programming background)
The function "double()" seems working fine without me have to reassign the value of the list used as an argument for the function and the value of the elements processed by the function would double as planned. Without the need to assign it outside the function.
However, the function "only_upper()" would require me to assign the list passed as argument through the function in order to see the effect of the function. I have to specify t=only_upper(t) outside of the function to see the effect.
So my question is this: Why are these two seemingly same function produces different result from the use of return?
Please explain in terms as plain as possible due to my inadequate programming skill. Thank you for your input.
def double(x):
for i in range(len(x)):
x[i] = int(x[i])*2
return x
x = [1, 2, 3]
print double(x)
def only_upper(t):
res = []
for s in t:
if s.isupper():
res.append(s)
t = res
return t
t = ['a', 'B', 'C']
t = only_upper(t)
print t
i am assuming that this is your first programming language hence the problem with understanding the return statement found in the functions.
The return in our functions is a means for us to literally return the values we want from that given 'formula' AKA function. For example,
def calculate(x,y):
multiply = x * y
return multiply
print calculate(5,5)
the function calculate defines the steps to be executed in a chunk. Then you ask yourself what values do you want to get from that chunk of steps. In my example, my function is to calculate the multiplied value from 2 values, hence returning the multiplied value. This can be shorten to the following
def calculate(x,y):
return x * y
print calculate(5,5)