Showing posts with label Python-features. Show all posts
Showing posts with label Python-features. Show all posts

Thursday, April 18, 2019

Python's dynamic nature: sticking an attribute onto an object


- By Vasudev Ram - Online Python training / SQL training / Linux training



Hi, readers,

[This is a beginner-level Python post.]

Python, being a dynamic language, has some interesting features that some static languages may not have (and vice versa too, of course).

One such feature, which I noticed a while ago, is that you can add an attribute to a Python object even after it has been created. (Conditions apply.)

I had used this feature some time ago to work around some implementation issue in a rudimentary RESTful server that I created as a small teaching project. It was based on the BaseHTTPServer module.

Here is a (different) simple example program, stick_attrs_onto_obj.py, that demonstrates this Python feature.
My informal term for this feature is "sticking an attribute onto an object" after the object is created.

Since the program is simple, and there are enough comments in the code, I will not explain it in detail.
# stick_attrs_onto_obj.py

# A program to show:
# 1) that you can "stick" attributes onto a Python object after it is created, and
# 2) one use of this technique, to count the number# of calls to a function.

# Copyright 2019 Vasudev Ram
# Web site: https://vasudevram.github.io
# Blog: https://jugad2.blogspot.com
# Training: https://jugad2.blogspot.com/p/training.html
# Product store: https://gumroad.com/vasudevram
# Twitter: https://twitter.com/vasudevram

from __future__ import print_function

# Define a function.
def foo(arg):
    # Print something to show that the function has been called.
    print("in foo: arg = {}".format(arg))
    # Increment the "stuck-on" int attribute inside the function.
    foo.call_count += 1

# A function is also an object in Python.
# So we can add attributes to it, including after it is defined.
# I call this "sticking" an attribute onto the function object.
# The statement below defines the attribute with an initial value, 
# which is changeable later, as we will see.
foo.call_count = 0

# Print its initial value before any calls to the function.
print("foo.call_count = {}".format(foo.call_count))

# Call the function a few times.
for i in range(5):
    foo(i)

# Print the attribute's value after those calls.
print("foo.call_count = {}".format(foo.call_count))

# Call the function a few more times.
for i in range(3):
    foo(i)

# Print the attribute's value after those additional calls.
print("foo.call_count = {}".format(foo.call_count))

And here is the output of the program:
$ python stick_attrs_onto_obj.py
foo.call_count = 0
in foo: arg = 0
in foo: arg = 1
in foo: arg = 2
in foo: arg = 3
in foo: arg = 4
foo.call_count = 5
in foo: arg = 0
in foo: arg = 1
in foo: arg = 2
foo.call_count = 8

There may be other ways to get the call count of a function, including using a profiler, and maybe by using a closure or decorator or other way. But this way is really simple. And as you can see from the code, it is also possible to use it to find the number of calls to the function, between any two points in the program code. For that, we just have to store the call count in a variable at the first point, and subtract that value from the call count at the second point. In the above program, that would be 8 - 5 = 3, which matches the 3 that is the number of calls to function foo made by the 2nd for loop.

Enjoy.

- Vasudev Ram - Online Python training and consulting

I conduct online courses on Python programming, Unix / Linux commands and shell scripting and SQL programming and database design, with course material and personal coaching sessions.

The course details and testimonials are here.

Contact me for details of course content, terms and schedule.

Try FreshBooks: Create and send professional looking invoices in less than 30 seconds.

Getting a new web site or blog, and want to help preserve the environment at the same time? Check out GreenGeeks.com web hosting.

Sell your digital products via DPD: Digital Publishing for Ebooks and Downloads.

Learning Linux? Hit the ground running with my vi quickstart tutorial. I wrote it at the request of two Windows system administrator friends who were given additional charge of some Unix systems. They later told me that it helped them to quickly start using vi to edit text files on Unix. Of course, vi/vim is one of the most ubiquitous text editors around, and works on most other common operating systems and on some uncommon ones too, so the knowledge of how to use it will carry over to those systems too.

Check out WP Engine, powerful WordPress hosting.

Creating online products for sale? Check out ConvertKit, email marketing for online creators.

Teachable: feature-packed course creation platform, with unlimited video, courses and students.

Posts about: Python * DLang * xtopdf

My ActiveState Code recipes

Follow me on:


Thursday, April 27, 2017

Using nested conditional expressions to classify characters

By Vasudev Ram


While writing some Python code, I happened to use a conditional expression, a Python language feature.

Conditional expressions are expressions (not statements) that have if/else clauses inside them, and they evaluate to either one of two values (in the basic case), depending on the value of a boolean condition. For example:
for n in range(4):
    print n, 'is odd' if n % 2 == 1 else 'is even'
0 is even
1 is odd
2 is even
3 is odd
Here, the conditional expression is this part of the print statement above:
'is odd' if n % 2 == 1 else 'is even'
This expression evaluates to 'is odd' if the condition after the if is True, and evaluates to 'is even' otherwise. So it evaluates to a string in either case, and that string gets printed (after the value of n).

Excerpt from the section about conditional expressions in the Python Language Reference:

[
conditional_expression ::= or_test ["if" or_test "else" expression]
expression ::= conditional_expression | lambda_expr

Conditional expressions (sometimes called a “ternary operator”) have the lowest priority of all Python operations.

The expression x if C else y first evaluates the condition, C (not x); if C is true, x is evaluated and its value is returned; otherwise, y is evaluated and its value is returned.
]

You can see that the definition of conditional_expression is recursive, since it is partly defined in terms of itself (via the definition of expression).

This implies that you can have recursive or nested conditional expressions.

Also, since the syntax of the Python return statement is:
return [ expression_list ]
(where expression_list means one or more expressions, separated by commas, it follows that we can use a nested conditional expression in a return statement (because a nested conditional expresssion is an expression).

Here is a small program to demonstrate that:
'''
File: return_with_nested_cond_exprs.py 
Purpose: Demonstrate nested conditional expressions used in a return statement, 
to classify letters in a string as lowercase, uppercase or neither.
Also demonstrates doing the same task without a function and a return, 
using a lambda and map instead.
Author: Vasudev Ram
Copyright 2017 Vasudev Ram
Web site: https://vasudevram.github.io
Blog: https://jugad2.blogspot.com
'''

from __future__ import print_function
from string import lowercase, uppercase

# Use return with nested conditional expressions inside a function, 
# to classify characters in a string as lowercase, uppercase or neither:
def classify_char(ch):
    return ch + ': ' + ('lowercase' if ch in lowercase else \
    'uppercase' if ch in uppercase else 'neither')

print("Classify using a function:")
for ch in 'AaBbCc12+-':
    print(classify_char(ch))

print()

# Do it using map and lambda instead of def and for:
print("Classify using map and lambda:")

print('\n'.join(map(lambda ch: ch + ': ' + ('lowercase' if ch in lowercase else 
'uppercase' if ch in uppercase else 'neither'), 'AaBbCc12+-')))
Running it with:
$ python return_with_nested_cond_exprs.py
gives this output:
Classify using a function:
A: uppercase
a: lowercase
B: uppercase
b: lowercase
C: uppercase
c: lowercase
1: neither
2: neither
+: neither
-: neither

Classify using map and lambda:
A: uppercase
a: lowercase
B: uppercase
b: lowercase
C: uppercase
c: lowercase
1: neither
2: neither
+: neither
-: neither
As you can see from the code and the output, I also used that same nested conditional expression in a lambda function, along with map, to do the same task in a more functional style
.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Are you a blogger with some traffic? Get Convertkit:

Email marketing for professional bloggers



Friday, March 24, 2017

Analysing that Python code snippet

By Vasudev Ram

Hi readers,

Some days ago I had written this post:

Analyse this Python code snippet

in which I had shown a snippet of Python code (run in the Python shell), and said:

"Analyse the snippet of Python code below. See what you make of it. I will discuss it in my next post."

I am a few days late in discussing it; sorry about that.

Here is the analysis:

First, here's the the snippet again, for reference:
>>> a = 1
>>> lis = [a, 2 ]
>>> lis
[1, 2]
>>> lis = [a, 2 ,
... "abc", False ]
>>>
>>> lis
[1, 2, 'abc', False]
>>> a
1
>>> b = 3
>>> lis
[1, 2, 'abc', False]
>>> a = b
>>> a
3
>>> lis
[1, 2, 'abc', False]
>>> lis = [a, 2 ]
>>> lis
[3, 2]
>>>

The potential for confusion (at least, as I said, for newbie Pythonistas) lies in these apparent points:

The variable a is set to 1.
Then it is put into the list lis, along with the constant 2.
Then lis is changed to be [a, 2, "abc", False].
One might now think that the variable a is stored in the list lis.
The next line prints its value, which shows it is 1.
All fine so far.
Then b is set to 3.
Then a is set to b, i.e. to the value of b.
So now a is 3.
But when we print lis again, it still shows 1 for the first item, not 3, as some might expect (since a is now set to 3).
Only when we run the next line:
lis = [a, 2]
and then print lis again, do we see that the first item in lis is now 3.

This has to do with the concept of naming and binding in Python.

When a Python statement like:
a = 1
is run, naming and binding happens. The name on the left is first created, and then bound to the (value of the) object on the right of the equals sign (the assignment operator). The value can be any expression, which, when evaluated, results in a value (a Python object [1]) of some kind. In this case it is the int object with value 1.

[1] Almost everything in Python is an object, like almost everything in Unix is a file. [Conditions apply :)]

When that name, a, is used in an expression, Python looks up the value of the object that the name is bound to, and uses that value in the expression, in place of the name.

So when the name a was used inside any of the lists that were bound to the name lis, it was actually the value bound to the name a that was used instead. So, the first time it was 1, so the first item of the list became 1, and stayed as 1 until another binding of some other (list) object to the name lis was done.

But by this time, the name a had been rebound to another object, the int 3, the same one that name b had been earlier bound to just before. So the next time that the name lis was bound to a list, that list now included the value of the current object that name a was now bound to, which was 3.

This is the reason why the code snippet works as it does.

On a related note (also about Python language features, syntax and semantics), I was playing around with the pprint module (Python's pretty-printer) and the Python is operator, and came up with this other snippet:

>>> import pprint
>>> lis = []
>>> for i in range(10):
... lis.append(lis)
...
>>> print lis
[[...], [...], [...], [...], [...], [...], [...], [...], [...], [...]]

>>> pprint.pprint(lis)
[<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>,
<recursion on list with id=32809968>]

>>> len(lis)
10

>>> lis is lis[0]
True

>>> lis is lis[0] is lis[0][0]
True

>>> lis is lis[0] is lis[0][0] is lis[0][0][0]
True

in which I created a list, appended it to itself, and then used pprint.pprint on it. Also used the Python is operator between the list and its 0th item, recursively, and was interested to see that the is operator can be used in a chain. I need to look that up (pun intended).

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Are you a blogger with some traffic? Get Convertkit:

Email marketing for professional bloggers



Saturday, March 11, 2017

Analyse this Python code snippet

By Vasudev Ram

Hi reader [1],

Analyze the snippet of Python code below. See what you make of it.

I will discuss it in my next post.

>>> a = 1
>>> lis = [a, 2 ]
>>> lis
[1, 2]
>>> lis = [a, 2 ,
... "abc", False ]
>>>
>>> lis
[1, 2, 'abc', False]
>>> a
1
>>> b = 3
>>> lis
[1, 2, 'abc', False]
>>> a = b
>>> a
3
>>> lis
[1, 2, 'abc', False]
>>> lis = [a, 2 ]
>>> lis
[3, 2]
>>>

[1] This product is suitable for Pythonistas aged 1 to 2 (approximately). For those of higher age, the dose may have no effect :)

- Vasudev Ram - Online Python training and consulting

Are you a blogger with some traffic? Get Convertkit:

Email marketing for professional bloggers

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter



Monday, January 16, 2017

Classifying letters and counting their frequencies

By Vasudev Ram

Here is a program that takes a string as input and classifies the characters in it, into vowels or consonants. It also counts the frequencies of each vowel separately and the frequencies of all consonants together - it is a contrived problem, of course, for teaching purposes.

I gave it as an example / exercise for a Python class recently, then modified / enhanced it slightly for this post.

It is fairly simple, but happens, partly by chance, to illustrate the use of multiple Python language features in under 35 or so lines of code.
I say "partly by chance" because, after writing it initially and then noticing that it used multiple language features, I thought of, and added a few more (for the same program functionality as earlier), trying not to be too artificial or contrived about it :)

(The statement that creates the input string s, is of course contrived, but it does manage to illustrate the ''.join(lis) idiom, and string 'multiplication' - and also use of backslash to continue lines, if you can call that a language feature. Also, the string multiplication (as used), though contrived, does allow us to quickly find the frequencies of either vowels or consonants, so it has a use.)

Some of the Python language features used are:
- dictionary comprehensions (dict comps)
- continue statement (beginners sometimes ask what it is used for)
- the .get() method of dicts - the 2-argument version, that allows you to avoid an if/else when counting frequencies
- returning multiple values from a function
- tuple unpacking (of the multiple values returned as a tuple)
- the ''.join(lis) idiom to join the characters (or strings) in a list, into a single string
- string 'multiplication' by an integer (a shorthand for repeating the string n times)
- assert statements for checking post-conditions

Here is the program, classify_letters1.py:
# classify_letters1.py
# Classify input characters as vowels or consonants.
# Count frequencies of each vowel.
# Count total frequency of all consonants together.
# Author: Vasudev Ram
# Copyright 2017 Vasudev Ram
# Web site: https://vasudevram.github.io
# Blog: https://jugad2.blogspot.com
# Product store: https://gumroad.com/vasudevram

import string

VOWELS = 'aeiou'

def classify_letters(input):
    vowel_freqs = { vowel: 0 for vowel in VOWELS }
    consonants = 0
    for c in input:
        if not (c in string.ascii_lowercase):
            continue
        if c in VOWELS:
            vowel_freqs[c] = vowel_freqs.get(c, 0) + 1
        else:
            consonants += 1
    return vowel_freqs, consonants

s = ''.join(['a' * 1, 'b' * 1, 'c' * 2, 'd' * 3, 'e' * 2, 'f' * 4, \
    'g' * 5, 'h' * 6, 'i' * 3, 'j' * 7, 'k' * 8, 'l' * 9, 'm' * 10, \
    'n' * 11, 'o' * 4, 'p' * 12, 'q' * 13, 'r' * 14, 's' * 15, \
    't' * 16, 'u' * 5, 'w' * 17, 'y' * 18, 'z' * 19])

print "Classifying letters in string:", s
print '-' * 70
vowel_freqs, consonants = classify_letters(s)
print 'vowel freqs:', vowel_freqs
print 'consonants total freq:', consonants
print '-' * 70
print 'Checking results:'
assert len(s) == sum(vowel_freqs.values()) + consonants
print 'OK'
And here is the output on running it:
$ py -2 classify_letters1.py
Classifying letters in string: abccdddeeffffggggghhhhhhiiijjjjjjjkkkkkkkklllllll
llmmmmmmmmmmnnnnnnnnnnnooooppppppppppppqqqqqqqqqqqqqrrrrrrrrrrrrrrssssssssssssss
sttttttttttttttttuuuuuwwwwwwwwwwwwwwwwwyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzz
----------------------------------------------------------------------
vowel freqs: {'a': 1, 'i': 3, 'e': 2, 'u': 5, 'o': 4}
consonants total freq: 190
----------------------------------------------------------------------
Checking results:
OK
I used an assert to do a sanity check of the values computed with the number of letters in the original string.
Putting the print 'OK' after the assert has a nice side effect that if the assert does not trigger, the program prints OK, but if it does trigger, it does not print OK, but an error message.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Managed WordPress Hosting by FlyWheel