Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New tricks #1

Open
brennerm opened this issue Mar 19, 2015 · 54 comments
Open

New tricks #1

brennerm opened this issue Mar 19, 2015 · 54 comments

Comments

@brennerm
Copy link
Owner

No description provided.

@agumonkey
Copy link

Tuples

tuple syntax

a = 1

# >>> a
# 1
# >>> a, 
# (1, )

unpacking iterable of length 1

a, = (1, )

unpacking with starred expression

a,*b,c = range(10)

# >>> a
# 0
# >>> b
# [1, 2, 3, 4, 5, 6, 7, 8]
# >>> c
# 9

a,*b,d,e,f = range(10)

# >>> (a,b,c,d,e,f)
# (0, [1, 2, 3, 4, 5, 6], 9, 7, 8, 9)

@obeleh
Copy link

obeleh commented Mar 19, 2015

loop over dicts that share (some) keys

dctA = {'a': 1, 'b': 2, 'c': 3}
dctB = {'b': 4, 'c': 5, 'd': 6}

for ky in set(dctA) & set(dctB):
    print ky

this one is much slower:

for ky in dctA:
    if ky in dctB:
        print ky

@obeleh
Copy link

obeleh commented Mar 19, 2015

lookup deep nested value in dicts

nested = {'a': {'b': {'c': {'d': 1}}}}

print nested.get('a', {}).get('b', {}).get('c', {}).get('d')
print nested.get('x', {}).get('y', {}).get('z', {}).get('0')

This is almost just as fast as completly testing whether the variable exists and IMO more readable.

@obeleh
Copy link

obeleh commented Mar 19, 2015

Copy lists

a = [1, 2, 3, 4, 5]
cpyA = list(a)
cpyB = a[:]

cpyB is faster

@obeleh
Copy link

obeleh commented Mar 19, 2015

Startswith

lst = range(10000)


def string_startswith():
    for _ in lst:
        'blasdfsf'.startswith('__')
        '__aqqqqqqqqqqqqqqqqqsdfsf'.startswith('__')


def string_eq_first_two():
    for _ in lst:
        'blasdfsf'[:2] == '__'
        '__aqqqqqqqqqqqqqqqqqsdfsf'[:2] == '__'

The second one is faster

@brennerm
Copy link
Owner Author

Hey agumonkey,
thanks for your contribution. I added your last example to the collection.
Your first two examples doesn't really show off a trick to me, that's why I didn't add them.
Anyway, thanks for your support.

@brennerm
Copy link
Owner Author

Hey obeleh,
thanks for your contribution. From your examples I added two to the collection.
The remaining are good to know as well, but I'm not looking for code snippets, that concentrate on performance.
Anyway, thanks for your support.

@agumonkey
Copy link

@brennerm

They sit between tricks and easy to overlook syntax. Here's another thing I only found recently:

slice operator accepts None

# useful to have a value to denote unknown lengths
#
# here with range pairs

ranges = [(0,6), (6,None)]
for beg,end in ranges:
    print('aabbccddeeff'[beg:end])

@brennerm
Copy link
Owner Author

@agumonkey
Yeah you can use [0:None], but you can write it like this as well:

print('aabbccddeeff'[0:])

For me this is more readable.

@agumonkey
Copy link

@brennerm

It's unrelated, ability to pass a a value to [:] is rarely shown and very valuable. (see this example).

@bash-j
Copy link

bash-j commented Mar 20, 2015

Never seen this trick before so I played around a bit.
Named formatting, more examples:

d = {'Jeff Smith': {'name': 'Jeff', 'age': 24}}
person = 'Jeff Smith'
print("My name is {name} and I'm {age} years old.".format(**d[person]))

Is it possible to do something like the following (which doesn't work)?

d = {'Jeff Smith': {'name': 'Jeff', 'age': {'years': 24, 'decades': 2.4}}}
person = 'Jeff Smith'
period = 'decades'
print("Hi my name is {name} and I'm {age[{0}]} {0} old.".format(period,**d[person]))

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-95-ff05fa5df228> in <module>()
      1 period = 'decades'
----> 2 print('Hi my name is {name} and I\'m {age[{0}]} {0} old.'.format(period,**d[p]))

KeyError: '{0}'

@brennerm
Copy link
Owner Author

@agumonkey
Agree, I will add an array slicing example

@brennerm
Copy link
Owner Author

@bash-j
I don't really understand what you' re trying to accomplish, cause your code doesn't make sense to me.
What is your expected output?

@bash-j
Copy link

bash-j commented Mar 20, 2015

@brennerm
I would like to pass the argument 'decades' as the dict key and description so it prints, 'Hi my name is Jeff and I'm 2.4 decades old.'

@agumonkey
Copy link

@brennerm

I think @bash-j wants to be able to use nested dicts, and asks if format has a syntax to address sub-elements.

@brennerm
Copy link
Owner Author

The way to access nested dicts is like this:

d = {'Jeff Smith': {'name': 'Jeff', 'age': {'years': 24, 'decades': 2.4}}}
"My name is {name} and I'm {age[decades]} decades old.".format(**d['Jeff Smith'])

But I suppose he wants to use the corresponding 'years' and 'decade' string as well.

@agumonkey
Copy link

@brennerm

Oh I see, accessing both key and value. I agree it's not a great idea. Better have ... 'age' : {'value' : 24, 'type' : 'years'} ... Anyway, I didn't remember the '{key[subkey]}' syntax.

Tried it with 2 level nested dicts :

d = {'name':'john', 'age': {'years' : 24, 'type': {'name': 'year(s)'}}}
'{name} is {age[years]} {age[type][name]}'.format(**d)

N level nested dicts works too : {root[sub][subsub][subsubsub]}.

@bash-j
Copy link

bash-j commented Mar 20, 2015

I thought that format would replace the {0} in age[{0}] with 'decades' first before attempting the dictionary look up.

I found that chaining two format functions together works:

print("Hi my name is {{name}} and I'm {{age[{0}]}} {0} old.".format('decades').format(**d['Jeff Smith']))

@kzinglzy
Copy link

Trick for socket.recv

coon = socket.connect(bind_address)
msgs = []

# normal way
while True:
    msg = coon.recv(1024)
    if recv:
        msgs.append(msg)
    else:  # when no msg come, break
         break

# hack way with iter and functools.partial
# this circle will auto break when msg is empty ''
for msg in iter(functools.partial(coon.recv, 1024), b'')):
    msgs.append(msg)

@kzinglzy
Copy link

property cache with Descriptor

   class PropertyCache(object):

    """ a decorator to cache property
    """

    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls):
        if not obj:
            return self
        value = self.func(obj)
        setattr(obj, self.func.__name__, value)
        return value

@tutoringsteve
Copy link

compound Boolean statements shortening trick:

Requiring two (or more) objects both be in a given set required as many "object in set ?" Boolean statements coupled by an and, repeating the same set name for each object. The resulting compound Boolean statement can be long especially when there are many objects and/or the set being tested has a long name.
Normally you'd have to write:

if A in my_dictionary[key].adjacent_nodes and B in my_dictionary[key].adjacent_nodes:
    do_stuff()

I found that with python, you can easily eliminate all the redundant references to the same set in these compound Boolean statements:

if A and B in my_dictionary[key].adjacent_nodes: 
    do_stuff()

adding a check for another item in the same set would be just as easy, add minimal verbosity, and be more readable using the trick:

if A and B and not C in my_dictionary[key].adjacent_nodes

I basically tried it because trying code is so easy in python, and it worked, I was pleasantly surprised but this could easily be very common knowledge that I missed in my readings. Either way, thank you all for gathering this collection of tricks, many of which were new to me and their usefulness immediately apparent.

@dharmit
Copy link
Contributor

dharmit commented Mar 21, 2015

A simple calculator created using operator module.

#!/usr/bin/env python3
# Created with help of an answer on stackoverflow. Don't have the exact link.

import operator
ops = {
    "+": operator.add,
    "-": operator.sub,
    "/": operator.truediv,
    "*": operator.mul
}

x = input("Enter an operator [OPTIONS: +, -, *, /]: ")
y = int(input("Enter number: "))
z = int(input("Enter number: "))

print (ops[x](y, z))

@agumonkey
Copy link

@dharmit

Funny, conditionalfunctional.py inspired writing the similar thing, albeit not interactive, more an assembler POV:

import operator

ops = {
    'add': operator.add,
    'sub': operator.sub,
    'mul': operator.mul,
    'div': operator.truediv
}

def run(op, a, b):
    return ops.get(op, 1)(int(a), int(b))

for inst in [
        'add 1 2',
        'mul 3 4',
        'div 121 11',
        'sub 9 10'
]:
    v = run(*(inst.split()))
    print(v)

@illuz
Copy link
Contributor

illuz commented Mar 21, 2015

Using globals().update() to set global variables from a dict, instead of using for loop and exec().

#! /usr/bin/env python3
"""set global variables from dict"""

d = {'a': 1, 'b': 'var2', 'c': [1, 2, 3]}
globals().update(d)
print(a, b, c)

@illuz
Copy link
Contributor

illuz commented Mar 21, 2015

A simple way to remove duplicate items in a list.

#! /usr/bin/env python3
"""remove duplicate items from list"""

items = [1, 2, 2, 3, 3, 3]

newitems = list(set(items))
print(newitems)

@brennerm
Copy link
Owner Author

merged your pull request and added you to the CONTRIBUTORS ;)

@brennerm
Copy link
Owner Author

@tutoringsteve
Thanks for your contribution!

@illuz
Copy link
Contributor

illuz commented Mar 21, 2015

Control white spaces in a string easily.

#! /usr/bin/env python3
"""control the whitespaces in string"""

s = 'The Little Price'

# justify string to be at least width wide
# by adding whitespaces
width = 20
s1 = s.ljust(width)
s2 = s.rjust(width)
s3 = s.center(width)
print(s1)   # 'The Little Price    '
print(s2)   # '    The Little Price'
print(s3)   # '  The Little Price  '

# strip whitespaces in two sides of string
print(s3.lstrip())  # 'The Little Price  '
print(s3.rstrip())  # '  The Little Price'
print(s3.strip())   # 'The Little Price'

@brennerm
Copy link
Owner Author

@kzinglzy
Please add an example that shows how to use the descriptor.

@kzinglzy
Copy link

@brennerm

example for the property cache decriptor:

class TestClass:

    @cache_property
    def property_to_be_cached(self):
        print('compute')
        return 'result'


ts = TestClass()
print 'first time:\n', ts.property_to_be_cached
print 'second time:\n', ts.property_to_be_cached

and the output will be:

first time:
compute!!
result
second time:
result

which the func just compute once : )

actually, the result has store in the TestClass.__dict__

@brennerm
Copy link
Owner Author

@kzinglzy
Thanks!

@kzinglzy
Copy link

By the way, @brennerm , it's necessary to check @tutoringsteve 's code.

It seems to be a mistake instead a trick.

Because:

# Code A
if A in my_dictionary[key].adjacent_nodes and B in my_dictionary[key].adjacent_nodes:
    do_stuff()

can not equal to be this !!!!!!!!!!

# Code B
if A and B in my_dictionary[key].adjacent_nodes:
    do_stuff()

and in fact Code B is equals to

if (A) and (B in my_dictionary[key].adjacent_nodes):
    do_stuff()

which means it doesn't check A in the dictonary

so, when A is not empty, it's seems right. And when A is empty, it doesn't work.

Here is a simple example.

L = [0, 1, 2, 3]
if 0 in L and 1 in L:
    print 'good'
else:
    print 'bad'
# output: good

if 0 and 1 in L:
    print 'good'
else:
    print 'bad'
# output: bad

@brennerm
Copy link
Owner Author

@kzinglzy
Yeah it's already removed ;)

@kzinglzy
Copy link

time to sleep, and there is a suggestion about the quality of trick

I think there is too many simple code, which can't be a trick and is just a normal using

for example:

# copylist.py

#! /usr/bin/env python3
"""a fast way to copy a list"""
a = [1, 2, 3, 4, 5]
print(a[:])

Do you think it's really a trick ?... = _ =

If it was, and this library will become a guide only for the one new to Python. = =!

that is all .
Thanks.

@tutoringsteve
Copy link

@kzinglzy Oh yes you're right haha, I see what happened. Wow, I need to be much more careful. Sorry about that and thanks for catching that!

Also, I agree with pruning the trick list some of them did seem to be 'common' but of course that's difficult to answer. The goal I believe should be surprising/novel and useful. The list copy is one of the first things you learn in almost any python resource that talks about lists. It also should mention that it copies without aliasing, so that the copy is not another reference to the same list. If it's going to be that beginner friendly.

@brennerm I still need to be removed from the contributor list, as I have not.

@brennerm
Copy link
Owner Author

@kzinglzy
I agree that this repository should not evolve into a tutorial on how to use Python.
The definition of what's a trick is currently only based on my opinion.

For sure there will be some examples that seem like normal usage to you, but contain additional value to others.
For example the standard way of copying a list I used was

list([1, 2, 3])

Considering that

[1, 2, 3][:]

is less obvious and twice as fast makes it a trick to me.

Nevertheless I'm open for every feedback and appreciate your opinion.

@smiren
Copy link

smiren commented Mar 23, 2015

#!/usr/bin/env python
"""
# We could make dict object more readable using json module

# Source
dic = {u'favorited': False, u'truncated': False, u'created_at': u'Mon Sep 24 03:35:21 +0000 2012', u'coordinates': None,
       u'entities': {u'user_mentions': [], u'hashtags': [{u'indices': [20, 34], u'text': u'freebandnames'}], u'urls': []},
       u'id_str': u'250075927172759552'}

# Output
{
    "coordinates": null,
    "created_at": "Mon Sep 24 03:35:21 +0000 2012",
    "entities": {
        "hashtags": [
            {
                "indices": [
                    20,
                    34
                ],
                "text": "freebandnames"
            }
        ],
        "urls": [],
        "user_mentions": []
    },
    "favorited": false,
    "id_str": "250075927172759552",
    "truncated": false
}
"""
import json

dic = {
    u'favorited': False, u'truncated': False, u'created_at': u'Mon Sep 24 03:35:21 +0000 2012', u'coordinates': None,
    u'entities': {u'user_mentions': [], u'hashtags': [{u'indices': [20, 34], u'text': u'freebandnames'}], u'urls': []},
    u'id_str': u'250075927172759552'
}
print(json.dumps(dic, sort_keys=True, indent=4))

# often for fast debug prints I use it like this:
# import json; print(json.dumps(dic_object, sort_keys=True, indent=4))

@smiren
Copy link

smiren commented Mar 23, 2015

This one isn't good enough:

#! /usr/bin/env python3
"""loop over dicts that share (some) keys"""

dctA = {'a': 1, 'b': 2, 'c': 3}
dctB = {'b': 4, 'c': 5, 'd': 6}

for ky in set(dctA) & set(dctB):
    print(ky)

Because in third python you could write simply:

for ky in dctA.keys() & dctB.keys():
    print(ky)
That is why in python3 keys()|values|items works like set too. In python2 for same purposes use should use view{keys,values,items}

@smiren
Copy link

smiren commented Mar 23, 2015

@illuz, for which kind of tasks, we need this dangerous trick?

#! /usr/bin/env python3
"""set global variables from dict"""

d = {'a': 1, 'b': 'var2', 'c': [1, 2, 3]}
globals().update(d)
print(a, b, c)

@brennerm
Copy link
Owner Author

@smiren
Not really sure what you mean with "view{keys,values,items}".
Added your option for Python3 users

@brennerm
Copy link
Owner Author

@axiaoxin
Updated README. Please only use the standard Python library.

@smiren
Copy link

smiren commented Mar 23, 2015

@brennerm, It is notation from shell(bash): echo view{keys,values,items} -> viewkeys viewvalues viewitems
In python2 dict has methods: viewkeys, viewvalues, viewitems. They can be used like sets

@isayme
Copy link

isayme commented Mar 27, 2015

#! /usr/bin/env python
"""ternary operator in python"""

cond = True
rel = "YES" if cond else "NO"
print(rel)  # YES

cond = False
rel = "YES" if cond else "NO"
print(rel)  # NO

@brennerm
Copy link
Owner Author

Hey @isayme,
this is already shown in conditionalassignment.py.
Anyway thanks for your contribution :)

@isayme
Copy link

isayme commented Mar 30, 2015

List some tricks, maybe help~

#! /usr/bin/env python

"""decorator"""
def wrap(func):
    def wrapper():
        return 'wrapper: ' + func()
    return wrapper

@wrap
def test():
    return 'hello world'

print(test())

"""clousre"""
def addBy(val):
    def func(inc):
        return val + inc

    return func

addFive = addBy(5)
print(addFive(4))

addThree = addBy(3)
print(addThree(7))    

"""lambda, """
square = lambda x : x**2
print(square(4))

"""yield generator"""
def gen():
    yield 'gen 1'
    yield 'gen 2'
    yield 'gen 3'
    yield 'gen 4'

g = gen()
for i in g:
    print i

"""list comprehension"""
arr = [x*2 for x in xrange(5)]
print(arr)

"""generator expression"""
arr = (x*3 for x in xrange(5))
print(arr)
for i in arr:
    print i

"""dictionary comprehension"""
arr = {'k'+str(x): 'v'+str(x*4) for x in xrange(5)}
print(arr)
print(arr.keys())
print(arr.values())

brennerm pushed a commit that referenced this issue Mar 30, 2015
@st0le
Copy link
Contributor

st0le commented Apr 1, 2015

A less known feature is ` operator calls the obj.__ repr __. (or repr(obj) )

Not sure if it's worth adding.

Edit: Github parses out `

@brennerm
Copy link
Owner Author

brennerm commented Apr 2, 2015

@st0le
Could you give me an example how to use the `?
I'm unable to find any information on this.

@brennerm
Copy link
Owner Author

brennerm commented Apr 2, 2015

@isayme
Added your closure example in nested_functions.py.
Thanks for your contribution :)

@st0le
Copy link
Contributor

st0le commented Apr 2, 2015

As I understand, the backticks were removed in Py3. But here's the gist, http://stackoverflow.com/a/20521892

@agumonkey
Copy link

complex unpacking

>>> [a],[b] = [[1],[2]]
>>> a,b
(1, 2)
>>> a
1
>>> b
2

even more complex unpacking

>>> [(c,*d,[*e]),f,*g] = [[1,2,3,4,[5,5,5]],6,7,8]
>>> (c,d,e,f,g)
(1, [2, 3, 4], [5, 5, 5], 6, [7, 8])

inspiration: https://twitter.com/renfredxh/status/586653125970386945

@brennerm
Copy link
Owner Author

@agumonkey
Copy link

@brennerm I know, but the old example didn't have list literals on the left hand side and no nesting.

@brennerm
Copy link
Owner Author

@agumonkey
Instead of

[a],[b] = [[1],[2]]

you could just do

a, b = 1, 2

which is way more readable IMO.
I'll add your second example to https://github.com/brennerm/PyTricks/blob/master/extendediterableunpacking.py

@agumonkey
Copy link

@brennerm yeah but unpacking is not only for hand made assignment, you may get [[1], [2]] as another function return value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests