<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Konstantin</title>
    <description>The latest articles on DEV Community by Konstantin (@xni).</description>
    <link>https://dev.to/xni</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F29103%2F6fff46c1-f21b-4fd2-8959-0b1c62008e89.jpeg</url>
      <title>DEV Community: Konstantin</title>
      <link>https://dev.to/xni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xni"/>
    <language>en</language>
    <item>
      <title>Man's Leadership</title>
      <dc:creator>Konstantin</dc:creator>
      <pubDate>Sat, 17 Aug 2019 18:23:44 +0000</pubDate>
      <link>https://dev.to/xni/leadership-in-the-office-2f0o</link>
      <guid>https://dev.to/xni/leadership-in-the-office-2f0o</guid>
      <description>&lt;p&gt;In my previous company we had an anonymous chat, and in the topic about relations most on the male employees (yes, the chat was anonymous, but it was obvious from the context that they were male) were saying something like: not sure I can have a normal relationships, I am an omega-male. Omega-male is the opposite to the alpha-male: you know them, focused, confident etc. Nowadays most of the western companies are focused on developing woman's leadership, but I do think that omega-males leadership abilities are also very very weak. Moreover,  being one of them (i am a male and I am attracted by women, but I am very shy, unconfident, passive, undecesive), I am starting panicking by trying to imagine myself being a leader. So, the question is: do women feel the same? Does all of those leadership programmes are breaking something in the souls? And if the answer is "yes, the training could change your leadership skills, but it doesn't hurt you", why no one is helping to omega-males? &lt;/p&gt;

</description>
      <category>leadership</category>
    </item>
    <item>
      <title>Solving the "Chicken-Egg" problem with Python</title>
      <dc:creator>Konstantin</dc:creator>
      <pubDate>Sat, 10 Aug 2019 21:10:52 +0000</pubDate>
      <link>https://dev.to/xni/solving-the-chicken-egg-problem-with-python-4ocn</link>
      <guid>https://dev.to/xni/solving-the-chicken-egg-problem-with-python-4ocn</guid>
      <description>&lt;p&gt;Hello!&lt;/p&gt;

&lt;p&gt;Nothing could be more tasty to Python than chicken and eggs. So today our Python decided to resolve an old phylosophical question: what was the first, chicken or egg. In order to do it, it has decided to create two classes and then to perform some modelling to make an ultimate answer to this question.&lt;/p&gt;

&lt;p&gt;Ok, let's start. Our Python is a grand-pa of all pythons, and it's serial number is 2.7. To begin with, it created 3 files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/egg.py

import models.chicken


class Egg(object):
    def __init__(self, name):
        super(Egg, self).__init__()
        self.name = name

    def wait(self):
        return models.chicken.Chicken("Chicken from [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/chicken.py

import models.egg


class Chicken(object):
    def __init__(self, name):
        super(Chicken, self).__init__()
        self.name = name

    def create_egg(self):
        return models.egg.Egg("Egg of [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: main.py

import models.chicken


if __name__ == '__main__':
    # Decision maker!
    c = models.chicken.Chicken("Ryaba")
    print(c.create_egg().wait().create_egg().name)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Grand-pa Python is happy with the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python2.7 main.py 
Egg of [Chicken from [Egg of [Ryaba]]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For those of you who are sceptical about the circular dependencies in Python - yes, they are supported, but only to some extent. Generally, when the interpreter sees &lt;code&gt;import models.egg&lt;/code&gt; it checks if this module is imported and if it is, uses the address of this module from the cache. If it is not, it immediately creates a record in the cache and then starts doing actual import. That's why if you only have &lt;code&gt;import &amp;lt;module_name&amp;gt;&lt;/code&gt; statements in your code you are safe.&lt;/p&gt;

&lt;p&gt;As soon as we decide to use &lt;code&gt;from &amp;lt;module_name&amp;gt; import &amp;lt;object&amp;gt;&lt;/code&gt; statement, our circular dependencies will not be able to be resolved. Let's try it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/egg.py

from models.chicken import Chicken


class Egg(object):
    def __init__(self, name):
        super(Egg, self).__init__()
        self.name = name

    def wait(self):
        return Chicken("Chicken from [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/chicken.py

from models.egg import Egg


class Chicken(object):
    def __init__(self, name):
        super(Chicken, self).__init__()
        self.name = name

    def create_egg(self):
        return Egg("Egg of [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python2.7 main.py 
Traceback (most recent call last):
  File "main.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken
  File "/data/models/chicken.py", line 1, in &amp;lt;module&amp;gt;
    from models.egg import Egg
  File "/data/models/egg.py", line 1, in &amp;lt;module&amp;gt;
    from models.chicken import Chicken
ImportError: cannot import name Chicken

$ python3.7 main.py 
Traceback (most recent call last):
  File "main.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken
  File "/data/models/chicken.py", line 1, in &amp;lt;module&amp;gt;
    from models.egg import Egg
  File "/data/models/egg.py", line 1, in &amp;lt;module&amp;gt;
    from models.chicken import Chicken
ImportError: cannot import name 'Chicken' from 'models.chicken' (/data/models/chicken.py)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is more or less predictable and it is a real chicken-egg problem. In order to import &lt;code&gt;Chicken&lt;/code&gt; class to the &lt;code&gt;egg&lt;/code&gt; module you need to parse &lt;code&gt;chicken&lt;/code&gt; module (so, just an address of a module is not enough), and in order to completely parse &lt;code&gt;chicken&lt;/code&gt; module, you need to complete parsing of an &lt;code&gt;egg&lt;/code&gt; module. So, no way.&lt;/p&gt;

&lt;p&gt;OK. And now I need your full attention: we are going to replace &lt;code&gt;import &amp;lt;package&amp;gt;.&amp;lt;module&amp;gt;&lt;/code&gt; by &lt;code&gt;from &amp;lt;package&amp;gt; import &amp;lt;module&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/egg.py

from models import chicken


class Egg(object):
    def __init__(self, name):
        super(Egg, self).__init__()
        self.name = name

    def wait(self):
        return chicken.Chicken("Chicken from [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/chicken.py

from models import egg


class Chicken(object):
    def __init__(self, name):
        super(Chicken, self).__init__()
        self.name = name

    def create_egg(self):
        return egg.Egg("Egg of [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the result is a bit unexpectable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python2.7 main.py 
Traceback (most recent call last):
  File "main.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken
  File "/data/models/chicken.py", line 1, in &amp;lt;module&amp;gt;
    from models import egg
  File "/data/models/egg.py", line 1, in &amp;lt;module&amp;gt;
    from models import chicken
ImportError: cannot import name chicken

$ python3.7 main.py 
Egg of [Chicken from [Egg of [Ryaba]]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, Python3 changed the way how packages are imported, generally they are appended to the sys.modules earlier than Python2. Good to know.&lt;/p&gt;

&lt;p&gt;And the last surprise for today. Now we are using &lt;code&gt;import &amp;lt;package&amp;gt;.&amp;lt;module&amp;gt; as &amp;lt;alias&amp;gt;&lt;/code&gt; syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/egg.py

import models.chicken as chicken


class Egg(object):
    def __init__(self, name):
        super(Egg, self).__init__()
        self.name = name

    def wait(self):
        return chicken.Chicken("Chicken from [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# filename: models/chicken.py

import models.egg as egg


class Chicken(object):
    def __init__(self, name):
        super(Chicken, self).__init__()
        self.name = name

    def create_egg(self):
        return egg.Egg("Egg of [{self.name}]".format(self=self))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And can you predict the output?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python2.7 main.py 
Traceback (most recent call last):
  File "main.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken
  File "/data/models/chicken.py", line 1, in &amp;lt;module&amp;gt;
    import models.egg as egg
  File "/data/models/egg.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken as chicken
AttributeError: 'module' object has no attribute 'chicken'

$ python3.6 main.py 
Traceback (most recent call last):
  File "main.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken
  File "/data/models/chicken.py", line 1, in &amp;lt;module&amp;gt;
    import models.egg as egg
  File "/data/models/egg.py", line 1, in &amp;lt;module&amp;gt;
    import models.chicken as chicken
AttributeError: module 'models' has no attribute 'chicken'

$ python3.7 main.py 
Egg of [Chicken from [Egg of [Ryaba]]]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Python3.7 won :) Ok. What is really happened here is that there was a &lt;a href="https://bugs.python.org/issue30024"&gt;bug&lt;/a&gt; in the &lt;code&gt;import A.B.C as D&lt;/code&gt; syntax which was &lt;a href="https://github.com/python/cpython/pull/3217/files"&gt;fixed&lt;/a&gt; in the version 3.7.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>python</category>
      <category>python37</category>
      <category>imports</category>
      <category>circulardependencies</category>
    </item>
    <item>
      <title>PyTest assertions</title>
      <dc:creator>Konstantin</dc:creator>
      <pubDate>Mon, 24 Jun 2019 21:25:39 +0000</pubDate>
      <link>https://dev.to/xni/pytest-assertions-1e0a</link>
      <guid>https://dev.to/xni/pytest-assertions-1e0a</guid>
      <description>&lt;p&gt;You have probably seen how PyTest reports errors:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_make_empty_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/tmp/empty_test"&lt;/span&gt;
    &lt;span class="n"&gt;make_empty_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s"&gt;'hello'&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="s"&gt;'hello'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt;      &lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But do you know how it does it? And have you ever seen the error like&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertion failed, but when it was re-run for printing intermediate values, it did not fail.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I do really recommend reading this article &lt;a href="http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html"&gt;http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pytest</category>
      <category>python</category>
      <category>unittesting</category>
    </item>
    <item>
      <title>The taste of Python</title>
      <dc:creator>Konstantin</dc:creator>
      <pubDate>Sat, 08 Jun 2019 12:28:05 +0000</pubDate>
      <link>https://dev.to/xni/the-taste-of-python-3p7j</link>
      <guid>https://dev.to/xni/the-taste-of-python-3p7j</guid>
      <description>&lt;p&gt;It is finally Friday! One more week has gone and looking back I can see that it was again spent mostly on exhausting code reviews. At least for me, it is a very time and energy consuming process. And what people are usually saying that it will be better. But I don't see any improvements. I am still leaving about 200 comments per 600-line PR. My colleagues are very smart and bright people having experience in different computer science areas from compilers to FPGA, from international consulting to quantum computers, but still, we are spending too much time on code reviews.&lt;/p&gt;

&lt;p&gt;So, as the first step towards our goal of reducing the time between pull request submission and merging, we connected &lt;a href="https://github.com/markstory/lint-review"&gt;lint-review&lt;/a&gt; to our GitHub repo. This helped, but not as much as I hoped.&lt;/p&gt;

&lt;p&gt;I was thinking about other things we could do differently to make our process better and faster. And what came to my mind was "a taste". After you tried Michelin restaurant you can not forget it. After you drove BMW sportscar you may still continue using you 13 y.o. Volkswagen, but you know that there is something more than that. I have no clue about the alcohol, but probably there is a difference between some rare wines and ones that you can buy in Tesco for 4 pounds per bottle.&lt;/p&gt;

&lt;p&gt;So, I was poisoned by the taste of great code. Fortunately, I worked with some brilliant engineers and they taught me something about the good code. What is my definition of greatness?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code should be exactly where you are expecting it to be.&lt;/li&gt;
&lt;li&gt;It should be as simple as possible, but not less than it is required.&lt;/li&gt;
&lt;li&gt;The authors are not trying to prove that they are smarter than the readers. They do not consider the code as a tool for self-expression but as a tool to make some job.&lt;/li&gt;
&lt;li&gt;The code should be written in a way that you can not distinguish who wrote it exactly. Do you know how long does it take to become a &lt;a href="https://en.wikipedia.org/wiki/Yeomen_Warders"&gt;Beefeater&lt;/a&gt;? 22 years of service in the army. After such a period you will probably act as a single person no matter what.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, having that said, I have decided to start doing something differently. Because I do care. Because I want us to succeed as a team. And to be honest I want to evolve as a developer and architect as well, and not to point to the similar problems over and over again.&lt;/p&gt;

&lt;p&gt;So, right now I am going to start a series of posts to show how good code can look like. And hopefully, it will help you to develop your mindset.&lt;/p&gt;

&lt;p&gt;I think I will write a series of posts, reviewing different projects and emphasizing some important details.&lt;/p&gt;

&lt;p&gt;And today we will start with a CPython standard library. I think it is the highest quality Python code you can ever find and in case of any doubts, I would recommend using it as an unconditional source of truth and arbiter.&lt;/p&gt;

&lt;p&gt;Let's take a look at the logging adapters. Hopefully, you are already familiar with loggers, handlers and formatters. LoggerAdapter is a less well-known class. So, we are going to read the source and decide what is it doing exactly without reading PEPs and documentation.&lt;/p&gt;

&lt;p&gt;Let's start!&lt;/p&gt;

&lt;p&gt;So, the first thing is to find the sources. &lt;a href="https://github.com/python/cpython/"&gt;https://github.com/python/cpython/&lt;/a&gt; to begin with. There are many folders on the top level.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls
CODE_OF_CONDUCT.md LICENSE            Misc               PCbuild            README.rst         config.sub         m4
Doc                Lib                Modules            Parser             Tools              configure          pyconfig.h.in
Grammar            Mac                Objects            Programs           aclocal.m4         configure.ac       setup.py
Include            Makefile.pre.in    PC                 Python             config.guess       install-sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, where our code is? Probably in &lt;code&gt;Lib&lt;/code&gt; or &lt;code&gt;Modules&lt;/code&gt;, what do you think? &lt;code&gt;ls Lib&lt;/code&gt;, &lt;code&gt;ls Modules&lt;/code&gt; makes it clear. Eventually, we are navigating to the &lt;code&gt;Lib/logging&lt;/code&gt; folder. Where are our adapters?&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls
__init__.py config.py   handlers.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are getting closer. Just a tiny comment before we will start reading the code. &lt;code&gt;logging&lt;/code&gt; is a pretty old module, dated back to 2001:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;commit 57102f861d506b6c2d2215d100dac9143574fa77
Author: Guido van Rossum &amp;lt;guido@python.org&amp;gt;
Date:   Wed Nov 13 16:15:58 2002 +0000

    Adding Vinay Sajip's logging package.

&lt;span class="gh"&gt;diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
&lt;/span&gt;new file mode 100644
&lt;span class="gh"&gt;index 0000000000..e0f2de5dd2
&lt;/span&gt;&lt;span class="gd"&gt;--- /dev/null
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/Lib/logging/__init__.py
&lt;/span&gt;&lt;span class="gu"&gt;@@ -0,0 +1,1183 @@
&lt;/span&gt;&lt;span class="gi"&gt;+#! /usr/bin/env python
+#
+# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And because of this, the style could be a bit different from your expectations, but remember, that migration from Python 2 to 3 was already a pain, so, breaking compatibility with more modules probably was not a good idea.&lt;/p&gt;

&lt;p&gt;Ok, back to our original topic. Let's imagine that someone told us that there is &lt;code&gt;logging.LoggerAdapter&lt;/code&gt; class and we want to understand it better.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoggerAdapter(object):
    """  
    An adapter for loggers which makes it easier to specify contextual
    information in logging output.
    """

    def __init__(self, logger, extra):
        """  
        Initialize the adapter with a logger and a dict-like object which
        provides contextual information. This constructor signature allows
        easy stacking of LoggerAdapters, if so desired.

        You can effectively pass keyword arguments as shown in the
        following example:

        adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
        """
        self.logger = logger
        self.extra = extra
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What can we see from here?&lt;/p&gt;

&lt;p&gt;This is a pretty small class, it has two fields. It makes some code easier, but it is not expected to bring new functionality. Somehow, adapters could be nested.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def debug(self, msg, *args, **kwargs):
    """
    Delegate a debug call to the underlying logger.
    """
    self.log(DEBUG, msg, *args, **kwargs)

def info(self, msg, *args, **kwargs):
    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, the adapter has the same set of methods as a usual logger. But we are not using inheritance. It is a very Pythonic decision. We are writing an adapter. So we want to implement the desired interface, but overwrite the behaviour. Duck typing is our friend here. If at some point the underlying implementation of &lt;code&gt;self.logger&lt;/code&gt; will change, our Adapter will throw &lt;code&gt;AttributeError&lt;/code&gt; exception, instead of falling to the original non-decorated implementation if we would choose inheritance.&lt;/p&gt;

&lt;p&gt;Jumping to the next function:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def log(self, level, msg, *args, **kwargs):
    """
    Delegate a log call to the underlying logger, after adding
    contextual information from this adapter instance.
    """
    if self.isEnabledFor(level):
        msg, kwargs = self.process(msg, kwargs)
        self.logger.log(level, msg, *args, **kwargs)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So here we are just proxying everything to the underlying &lt;code&gt;self.logger&lt;/code&gt;, but patching &lt;code&gt;msg&lt;/code&gt; and &lt;code&gt;kwargs&lt;/code&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def process(self, msg, kwargs):
    """
    Process the logging message and keyword arguments passed in to
    a logging call to insert contextual information. You can either
    manipulate the message itself, the keyword args or both. Return
    the message and kwargs modified (or not) to suit your needs.

    Normally, you'll only need to override this one method in a
    LoggerAdapter subclass for your specific needs.
    """
    kwargs["extra"] = self.extra
    return msg, kwargs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, this is the core of the &lt;code&gt;LoggerAdapter&lt;/code&gt;. See, how thoughtful is it. Even if we are not patching &lt;code&gt;msg&lt;/code&gt; argument, we are still leaving the space for updating it to the classes derived from &lt;code&gt;LoggerAdapter&lt;/code&gt;. Another interesting option here, and it is clear from the code, that even if &lt;code&gt;kwargs&lt;/code&gt; had an &lt;code&gt;extra&lt;/code&gt; parameter we are overriding it. I like the name of the argument also, &lt;code&gt;kwargs&lt;/code&gt;. It is not a &lt;code&gt;**kwargs&lt;/code&gt;, it is just a usual dictionary argument, but because of this name, I feel that it is OK that we are modifying it in-place.&lt;/p&gt;

&lt;p&gt;Then there is a bunch of other methods, like:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def setLevel(self, level):
    """
    Set the specified level on the underlying logger.
    """
    self.logger.setLevel(level)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Again, could it be avoided by using inheritance? Yes, it could. But then we are losing control and &lt;code&gt;LoggerAdapter&lt;/code&gt; is not an adapter anymore. Moreover, right now, &lt;code&gt;LoggerAdapter&lt;/code&gt; is compatible with any logger with follows a protocol (has debug/info/warning and some other methods), it is not actually extending the &lt;code&gt;logging.Logger&lt;/code&gt; method, it can use your own logger as an underlying logger. That also adds more value to this class.&lt;/p&gt;

&lt;p&gt;This is it! We got everything we wanted to know just by reading the code less for 5 minutes. Let's try to use it!&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; import sys
&amp;gt;&amp;gt;&amp;gt; import logging
&amp;gt;&amp;gt;&amp;gt; logging.basicConfig(
        stream=sys.stdout,
        format='%(asctime)s %(req_id)s %(message)s',
        level=logging.DEBUG)
&amp;gt;&amp;gt;&amp;gt; logging.info('Hello', extra={'req_id': '123123'})
2019-06-08 00:41:24,420 123123 Hello
&amp;gt;&amp;gt;&amp;gt; my_logger = logging.LoggerAdapter(
        logging.getLogger(), {'req_id': '123123'})
&amp;gt;&amp;gt;&amp;gt; my_logger.info('world!')
2019-06-08 00:42:28,724 123123 world!
&amp;gt;&amp;gt;&amp;gt; my_logger.info('world!', extra={'req_id': 999999})
2019-06-08 13:27:35,105 123123 world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Was the code clear? Yes! Is it verbose? Yes, it is. But because of that, no one will really have any problems understanding or extending this class. Please, open the file and read the definition again: &lt;a href="https://github.com/python/cpython/blob/master/Lib/logging/__init__.py#L1745"&gt;https://github.com/python/cpython/blob/master/Lib/logging/__init__.py#L1745&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But again, the point here is not to write about one more Python class, it is much wider: please, take a look at the beautiful code. And we all know, that beautiful cannot be wrong.&lt;/p&gt;

&lt;p&gt;So, please, read the good code. It is as important as reading books. It is all of the best practices applied in action. And even when there are no good examples around, because we are on the greenfield project, and even if some of our internal libraries are not even close like that, now you know what my expectations are. And I hope after my series of posts you will not be able to write in any style other than that.&lt;/p&gt;

</description>
      <category>python</category>
      <category>codereviews</category>
    </item>
  </channel>
</rss>
