Click here to Skip to main content
15,886,799 members
Articles / All Topics
Technical Blog

Why I Don't Like Python Anymore

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
17 May 2013CPOL7 min read 14.5K   3   1
This blog discusses why I do not like Python anymore.

Introduction

It was few years back in my first year of studies when my friend told me about Python. Then, it was great for me. One-liners could read ftp folders, web pages, interact with operation system, list comprehension, injecting code into life programs, nested functions, procedural as object oriented programming styles, etc. All of this in builtin libraries with simple but yet quite functional editor, supported with great documentation. I wrote my first studies project in Python and I was proud with it a little Wink. During the following years, I was using Python for writing simple scripts to automate my professional work as PHP programmer. Python was and I still think is a better language than PHP. After that, I started to work as .NET/C# developer. It has a different syntax but C similarities let me learn it quite quickly. Framework .NET is a powerful tool also so I didn't touch Python for 2 or 3 years at least for anything longer than a few lines.

But a week or so ago, I decided to write a simple script to automate reading whole RSS channel. I wanted it to filter it for only the news that I was interested in. I decided to use Python for it. I was also considering PowerShell since it is built into Windows and it has a benefit from .NET. But I don't like its weird syntax, and since I wanted to just write this script and not struggle with syntax errors, I chose Python. I don't want to say that I made an mistake. It is still a powerful language and I manage to write this script quickly, but... there was some misunderstanding. Maybe I am more experienced. Or spoiled with C#, or used to it. Or just picky. Nonetheless, I decided to share this with the world.

One of the things was global versus local scope clash. I probably would get over it if not other things.

Let us consider a little program like this:

global_var= 1

def func_with_local_scope():
    print(global_var)

func_with_local_scope()
print(global_var)

I does a really simple thing: prints value '1' two times. Despite the fact that func has its own local scope aside from global scope, it will work because the interpreter will think about 'global_var' variable as global. It is correct behavior. Now let us change a bit:

global_var= 1

def inc():
    print(global_var)
    global_var+=1

inc()
print(global_var)

And... it will fail. Why?

  File "test.py", line 4, in inc
    print(global_var)
UnboundLocalError: local variable 'global_var' referenced before assignment

My first reaction was: "Huh?" How it is not assigned? It is done right before this function. Let's inspect scopes inside 'inc' function:

GLOBALS:

{'__name__': '__main__', 'global_var': 1, '__package__': 
None, '__doc__': None, 'inc': <function inc at
 0x00000000031EC488>, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__file__': 
'F:\\skrypty\\music_rss\\test.py', '__builtins__': <module 'builtins'>}

LOCALS:

{}

It is perfectly fine! Our variable is defined in global scope and it has value! And local scope is empty since I did not declare any local variables. Why system that is resolving variables names does not check for global scope when it is trying to find value? Because doing any assignment in function tells interpreter that this variable WILL be assigned in function and without declaring it with 'global' keyword, it WILL be a local variable. I know that. I understand that. And I think that is inconsistent and it should be done better. I would agree to always use the 'global' keyword or always not using and just letting interpreter do the job. Either way would be fine since it is consistent and designer of the language decided to this that way. It's his call. It could not be ideal but it would be intuitive.

Let's do one test:

global_var = 1

class Incrementer:
    print(global_var)
    global_var+=1
    print(global_var)
    def inc(self):
        print(global_var)
        print(self.global_var)

i = Incrementer()
i.inc()
print(global_var)

When I was writing this, I was expecting it to fail with my knowledge of scopes in functions. But I was also suspecting that this actually might work and I will find more scope inconsistencies. And there it is. I work just fine. I mean it runs but it certainly does that like Spanish Inquisition. The result will be:

1 
2
1
2
1

First '1' is print(global_var) inside class declaration. Despite the fact that variable with this name is defined later in class and since this would cause an error in function, I would expect it to fail here also. Here comes another 'Huh?'. Instead, it is resolved to global variable.

Second print is done after incrementing variable with the same name. Which is it? It is global variable? Or it is new local class variable? If code would be ended here, it would be good, tricky question for interview for Python job (maybe it is I don't know). Simple programmer like me would expect (if you reference variable with the same name in the same way, it SHOULD be the same variable). No. It is not.

Third print shows that to us. It is a global variable with the same name and it is still simple '1'. Doing incrementation in class scope of global variable (as I would expect) creates new local class variable and assigns to it value of global variable PLUS one. How in hell is this incrementation?

Forth print shows us the value of two and actual place of incremented value. It is known in class variable with same name as global variable. I will call this incrementation with displacement. Python-like.

Last print shows us global unchanged variable. It is still there, not touched.

Uhm... I don't know. Maybe this is explained somewhere in the documentation. Maybe it is logical somewhere in bowels of interpreter. Or whatever magic thing makes this work. I don't know. I don't have time or the will to dig into few hundreds of pages of documentation for specifics of inner workings of something so basic like arithmetical operators. And frankly, I don't care. Throw and error, when you trying to increment value that is not there - like C. Resolve it to global variable like in JS. Prevent to name the variable just like another variable in scope level above - like C#. Just pick one please. And be consistent.

Another one exists only in Python 3, contrary to previous. It begins with the decision that all strings should be unicode. That is great. Resolving encodings is a pain. But... console have its encoding too. I don't know how this works in other systems than Windows, but I suspect it's a problem too since when Googling for an answer I found that it happened not only to me. So what happens when you get some unicode char that can't be encoded in console specific encoding? It suppose to show some not readable characters like ¦+cŠc¦??¦ or just ? sings, don't you think? No. It throws exception. Program crash. End of story. It is command line. It is tool to interacting with users. If user will have to make something from information that 10% of it is gibberish that is bad not it is not critical. When application is throwing an error when encoding string to some specific encoding when saving data to for example to DB, it will probably be desired to throw error. But not in output! It is even more horrible because writing an app in Python, you cannot know for sure what will be user interface encoding so you cannot be prepared on anything with tool like this.

So script below will work fine in Python Shell fine and will fail in cmd.exe:

string = 'показано'

print(sys.stdout.encoding)
print(string)

With exception like that:

  File "C:\Python33\lib\encodings\cp852.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_map)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-7: cha
racter maps to <undefined>

I added line print(sys.stdout.encoding) so I know that Python internally knows what encoding is in output. This can be used to do something like this BEFORE print:

string.encode('cp852','replace')

So every char that can't be encoded will be replaced with something else. But why cannot this be done inside print? Why print at least cannot have another param to silently pass encoding errors?

Another one that is connected with 3 version. In previous releases, I used Python to debug http response of websites. It was a really nice tool for it with set_http_debuglevel method of HTTPHandler class.

send: 'GET / HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: <a href="http://www.wp.pl\r\nConnection">www.wp.pl\r\nConnection</a>: 
close\r\nUser-Agent: Python-urllib/2.7\r\n\r\n'
reply: 'HTTP/1.0 200 OK\r\n'
header: Server: aris
header: Expires: Thu, 07 Feb 2013 21:21:24 GMT
header: Last-Modified: Thu, 07 Feb 2013 21:21:24 GMT
header: Pragma: no-cache
header: Cache-Control: no-cache
header: Content-type: text/html; charset=UTF-8
header: Set-Cookie: reksticket=1360272084; expires=Sat, 09-Feb-2013 21:21:24 GMT; 
path=/; domain=.www.wp.pl
header: Set-Cookie: rekticket=1360272084; expires=Sat, 09-Feb-2013 21:21:24 GMT; 
path=/; domain=.wp.pl
header: Set-Cookie: statid=89.71.103.226.25161:1360272084:324990297:v1; path=/; 
expires=Sun, 07-Feb-16 21:21:24 GMT
header: Set-Cookie: statid=89.71.103.226.25161:1360272084:324990297:v1; 
domain=.wp.pl; path=/; expires=Sun, 07-Feb-16 21:21:24 GMT
header: Content-Length: 94719
header: Connection: close

It is not working in Python 3:

import urllib.request

h=urllib.request.HTTPHandler()
h.set_http_debuglevel(1)
b=urllib .request.build_opener(h)
a=b.open('http://www.wp.pl').readall()

This feature was not documented as far as I know. I have found this in 'Dive in Python' book (http://www.diveintopython.net/). I suspect this was not important enough to maintain it in new versions. So much that it was not even removed.

I could go on with lack of nice function for handling dates and time. Or 'self' (non)keyword inside class declarations. But this rant is long enough as it is. Maybe I will write another script in PowerShell, after fighting with syntax it will be more usable for simple scripts.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Thornik20-May-13 7:41
Thornik20-May-13 7:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.