Python patterns that have been released to the world and have been scrutinized later

Hello. I'm Nabeta, who is a software engineer in the infrastructure group. It's the night of the 11th (22:00), so I'm writing this article quite impatiently. I will continue to write after finding it until the last minute. Hit the keyboard as calmly as you don't have time, Luke.

Release top priority

Basically, the release does not wait except when it is quite dangerous, so I think that everyone has experience in the case of sending it out to the world as "No!". Also, I couldn't take the time to refactor including the existing code, or I wrote a strange code because of my lack of ability. After that, I will reflect on some of the patterns that I have fixed. Anyway, I don't have time, so I'll do my best to fill the space for articles!

Do not throw in \ _ \ _ data \ _ \ _ when creating an object

sample1.py


class Api(object):
    def __init__(self, init_data):
        self.init_data = init_data
        self.prepare()
        
    def prepare(self)
        for k, v in self.init_data.items():
            self.__data__[k] = v

ʻInit_data is a dictionary. Even if the inside of ʻinit_data that is eaten by ʻApi is changed, I do not have to change the implementation of ʻApi, and I often used it while thinking that it was metaprogramming and cool. If you read it, you have to know the internal implementation of ʻinit_data, and other classes and methods that implement using ʻApi objects, which is quite painful. If you generate ʻinit_data` in code, it will be really unpleasant to read.

It was easier to read later if I prepared a dictionary that defined all key and value, or created a class that wraps it.

sample2.py


class Api(object):
    def __init__(self, init_data):
        self.name = name
        self.a = init_data['a']
        self.b = init_data['b']
        ...

do not use else after the for, while loop

Python allows you to put ʻelse` after a block of loops.

sample3.py


for i in range(5):
    print('Count %d' % i)
else:
    print('ELSE BLOCK!!')

This is executed immediately after the loop ends. There is no feeling of ʻelseat all. Furthermore, this one is skipped whenbreak is done, so I don't understand the meaning of ʻelse anymore.

sample4.py


>>> for i in range(5):
...     if i == 3:
...         break
...     print(i)
... else:
...     print('ELSE!') # unreachable!
0
1
2

Then I was wondering what this guy was for, but I think it would be a little effective when processing something that meets the conditions in a loop. However, in that case, it is easier to read if you create a function for that. The name ʻelse` is misleading and should not be used.

The following is the one that scans and returns the result of whether or not the one you were looking for was found.

sample5.py


#This is better
def power_up_if(users):
    found = False
    for user in users:
        if user == 42: #Return as soon as the condition you are looking for is found
            found = True
            break
    return user, found

users = [1,2,3,41,42,5]
uid, found = power_up_if(users)
if found:
    print('uid:', uid, 'is power up!')

Do not write as if the default arguments are set dynamically

You will rarely want to use non-static keyword arguments as default values for functions and methods.

sample6.py


>>> def log_out(msg, when=datetime.datetime.now()):
...       print('%s %s ' % (when, msg))
... 
... log_out('Good bye!!')
... log_out('Good bye!!')
2016-12-11 21:21:20.928785 Good bye!!
2016-12-11 21:21:20.928785 Good bye!!

I think this is a specification that you will surely know (step on) if you write Python as it is. The default argument is evaluated only once when the function is defined. That is, the value of the default argument is determined when the module is loaded and will never be evaluated again. I have also stepped on this specification and paid a lot of money to solve the problem. As a workaround

sample7.py


>>> def log_out(msg, when=None):
...     """Output the log including the time stamp
...
...     Args:
...         msg:Log message
...         when:Date and time of log output. If not, set the current date and time.
...     """
...     when = datetime.datetime.now() if when is None else when
...     print('%s %s ' % (when, msg))

>>> log_out('hoge')
2016-12-11 21:28:58.293714 hoge

>>> log_out('bye')
2016-12-11 21:29:02.566320 bye

>>> log_out('bye', datetime.datetime(2016,11,1))
2016-11-01 00:00:00 bye

I think it's better to set the value of the default argument to None and determine it by the idiom of when = datetime.datetime.now () if when is None else when in the function. If there is, I will set it, and if not, I will do it here.

Choose public over private

As far as I know, there is no absolute private in Python.

sample8.py


>>> class A(object):
...     def __aa(self):
...         print('private')
>>>
>>>
>>> a = A()
>>> a._A__aa()
private
>>> class A(object):
...     def __init__(self, a):
...         self.__a = a

>>>
>>> a = A(123)
>>> print(a.__dict__)
{'_A__a': 123}

So anyone who knows the above compiler conversion rules can access private attributes. However, since private and public exist as the visibility of the class, there is no case where the above access is actually required, and if so, I think that I made a mistake in the design.

Then, the reason why everyone can access public like is that it is already based on the theory of sexuality. I'm wondering if that's what Python is so cute, but you probably think that the benefits of being public are higher than the benefits of being private. I'm not sure if this is correct, but lately the cases of making it private (giving the __start attribute) are mostly limited to:

--Being a parent class --The namespace is likely to conflict in a small class --name, val, etc.

sample9.py


>>> class Student(object):
...     def __init__(self):
...         self.__name = "Anonymous"
...
...     def get_name(self):
...         return self.__name
...
... class ChildStudent(Student):
...     def __init__(self):
...         super().__init__()
...         self._name = "Child"
...
... c = ChildStudent()
... print(c.get_name(), c._name)
Anonymous Child

Do not define get or set methods

This is by no means evil or anything like that, and you don't have to explicitly implement getters or setters.

sample10.py


>>> class OtherLangStyle(object):
...     def __init__(self, val):
...         self._val = val
...
...     def get_val(self):
...         return self._val
...
...     def set_val(self, new_val):
...         self._val = new_val

>>> ols = OtherLangStyle(1)
>>> ols.get_val()
1
>>> ols.set_val(2)
>>> ols.get_val()
2
>>> ols.set_val(ols.get_val() + 2)
>>> ols.get_val()
4

Not like Python. I feel like it's encapsulated. .. .. As mentioned above, I think that there is no problem if I simply implement it in public, and I always implement it in public.

sample11.py


>>> class PythonStyle(object):
...     def __init__(self, val):
...         self.val = val
... ps = PythonStyle(1)
... ps.val = 3
>>> ps.val
3
>>> ps.val += 4
>>> ps.val
7

If you need a hook for this attribute

If you need a hook later, Python has a super useful thing called a @property decorator, and you can just implement @property, setter.

sample12.py


>>> class Student(object):
...     def __init__(self, name, score):
...         self._name = name
...         self._score = score
...
...     @property
...     def score(self):
...         return self._score
...
...     @score.setter
...     def score(self, score):
...         before = self.score
...         self._score = score
...         self.notify_score_up(before)
...
...     @property
...     def name(self):
...         return self._name
...
...     def notify_score_up(self, before):
...         print('%s score is up. %d to %d.' %
...               (self.name, before, self.score))
...
... s = Student('nabetama', 0)
... s.score = 3
nabetama score is up. 0 to 3.

By implementing the setter like this, you can also check the passed value!

Use functools.wraps when creating decorators

When writing Python, everyone wants to write a decorator at least once. When you want to know the processing time of a function, etc.

sample13.py


>>> def checktime(func):
...     @wraps(func)
...     def wrapper(*args, **kwargs):
...         import time
...         start = time.time()
...         res = func(*args, **kwargs)
...         end = time.time() - start
...         print('--------------------------------------------------')
...         print(end)
...         print('--------------------------------------------------')
...         return res
...     return wrapper
...
... @checktime
... def wait1():
...     time.sleep(1)
...     print('owata')
...
... wait1()
owata
... print(wait1)
<function wait1 at 0x1119a0d90>    #wait1 and its metadata are returned
--------------------------------------------------
1.003669023513794
--------------------------------------------------

I prepare a decorator like this and do something like wrapping a function. The reason for using functools.wraps is that the value (that is, the function) returned by the decorator does not interpret itself as the calling method. Try removing wraps () from the above code.

sample14.py


>>> def checktime(func):
...     def wrapper(*args, **kwargs):
...         import time
...         start = time.time()
...         res = func(*args, **kwargs)
...         end = time.time() - start
...         print('--------------------------------------------------')
...         print(end)
...         print('--------------------------------------------------')
...         return res
...     return wrapper
...
... @checktime
... def wait1():
...     time.sleep(1)
...     print('owata')
...
... wait1()
... print(wait1)
owata
--------------------------------------------------
1.0004959106445312
--------------------------------------------------
<function checktime.<locals>.wrapper at 0x1118b5ae8>  #...?

You can use functools.wraps to copy the internal function metadata to the external function even if it is wrapped in a decorator.

Use built-in algorithms and data structures

Dictionary sort

Python dictionaries are not sorted. However, in a real program, you may want to sort the dictionary keys in the order in which they are inserted. In such a case, collection.OrderedDict is convenient.

sample15.py


>>> names = ['s', 'p', 'l', 'g']
>>> o = OrderedDict()
>>> for name in names:
...     o[name] = 1

>>> o
OrderedDict([('s', 1), ('p', 1), ('l', 1), ('g', 1)])

>>> oo = dict()
>>> for name in names:
...     oo[name] = 1
>>> oo
{'p': 1, 'l': 1, 'g': 1, 's': 1}

Default dictionary

It returns a pre-determined default value if the key does not exist.

sample16.py



>>> from collections import defaultdict
>>> rec = defaultdict(int)
>>> rec['ore']
0
>>> rec
defaultdict(<class 'int'>, {'ore': 0})

bonus

Recently, I enjoy writing documents with Sphinx, but in .git/hooks

sample.sh


#!/bin/sh
#
make html

If you do, I'm happy that the html build will run automatically every time.

Summary

I think it's not a lot, but if I read back the Python project I wrote this year, it will come out. It will come out more, and it's been 12th, so I'll put a brush on it. There are many other things, so I'll write it on my blog.

I mentioned it as soon as I came up with it, but I will say goodbye in 2016, thinking that I will reflect on this article later.

Recommended Posts

Python patterns that have been released to the world and have been scrutinized later
Say hello to the world with Python with IntelliJ
PHP and Python samples that hit the ChatWork API
I felt that I ported the Python code to C ++ 98.
The attitude that programmers should have (The Zen of Python)
[C / C ++] Pass the value calculated in C / C ++ to a python function to execute the process, and use that value in C / C ++.
Jedi-vim shortcut command that allows you to refer to the definition source and definition destination in Python
A story that makes it easy to estimate the living area using Elasticsearch and Python
[Python] A program to find the number of apples and oranges that can be harvested
Build a Python environment and transfer data to the server
I want to know the features of Python and pip
I tried to enumerate the differences between java and python
How to write a metaclass that supports both python2 and python3
We have released an extension that allows you to define xarray data like a Python data class.
Determine the date and time format in Python and convert to Unixtime
The story that the version of python 3.7.7 was not adapted to Heroku
How to pass and study the Python 3 Engineer Certification Basic Exam
Regular expressions that are easy and solid to learn in Python
A story that struggled to handle the Python package of PocketSphinx
How to get followers and followers from python using the Mastodon API
[Introduction to Python] I compared the naming conventions of C # and Python.
[Python] How to get the first and last days of the month
How to judge that the cross key is input in Python3
Use python on Raspberry Pi 3 to illuminate the LED (Hello World)
Try to write a program that abuses the program and sends 100 emails
About the bug that anaconda fails to import numpy and scipy
A script that returns 0, 1 attached to the first Python prime number
The road to installing Python and Flask on an offline PC
A quick guide to PyFlink that combines Apache Flink and Python
Convert the result of python optparse to dict and utilize it
Verification of the theory that "Python and Swift are quite similar"
Have python check if the string can be converted / converted to int
Try to implement and understand the segment tree step by step (python)
[Introduction to Python] How to use the Boolean operator (and ・ or ・ not)
[python] A note that started to understand the behavior of matplotlib.pyplot
[Python] A program that rotates the contents of the list to the left
How to display bytes in the same way in Java and Python