Generating Julia set fractal I am a total novice to Python and have been testing my skills recently. I found this particular challenge and wanted to give it a shot. I made this code and it functions well, but very slowly. I was looking for feedback on how to optimize in Python, as well as general Python best practices!

Main.py

from PIL import Image


IMGHeight = 2000
IMGWidth  = 2000

xmin = -2.0
xmax =  2.0

ymin = -2.0
ymax =  2.0


def Function(z):
    return z*z + complex(-0.221, -0.713)

def Pixel(point):
    #set up how the complex number will create a pixel

    limit = 255
    thresh = 2

    val = Function(point)
    for I in range(1, limit):       
        if(abs(val) &gt; thresh):
            return I

        val = Function(val)

    return limit


def main():
    myImg = Image.new('L', (IMGWidth+1,IMGHeight+1))

    for x in range(IMGWidth):
        zx = x * (xmax - xmin)/(IMGWidth - 1) + xmin
        for y in range(IMGHeight):
            zy = y * (ymax - ymin)/(IMGHeight - 1) + ymin
            z = complex(zx, zy)
            #pixels.append(Pixel(z))
            myImg.putpixel((x,y), Pixel(z))

    myImg.save('Fractals.png')

if __name__ == "__main__":
    main();
PEP 8

Please follow PEP 8, you ca use tools like pylint to find PEP 8 issues immediately. But for instance:

Function -&gt; function
Pixel    -&gt; pixel


There are other small related issues.

Docstrings

Python has docstrings which are ways to document your functions. It isn't exactly clear what Function or Pixel do at the moment.

You have a comment describing Pixel. Two things about this comment:


I don't understand it, what do you mean by "create a pixel"?
It is not, as of now a docstring, to make it one, use the triple quoted string and put right below the function declaration, so:




def Pixel(point):
    #set up how the complex number will create a pixel


Should be:

def Pixel(point):
    """Set up how the complex number will create a pixel"""


But, again you should probably reword the comment, at the moment the meaning is not so clear, possibly:

def Pixel(point):
    """Calculate the color threshold for a particular pixel"""


Generalization

I would argue that you should add an additional parameter to Pixel. Function is one of the many functions you could pass into the set so allow someone else to change it and add f as a parameter to Pixel and pass the function in.

Optimization

What would I change to optimize the code?*



Instead, I would first switch to PyPy (which uses JIT compilation to make the code, in this case, much faster) and run the program that way, install PyPy, then (this worked for me):

pypy -m pip install Pillow


to install Pillow on pypy run it. Here are the time comparisons on my computer:

➜  codereview time pypy julia.py
pypy julia.py  3.10s user 0.07s system 99% cpu 3.198 total
➜  codereview time python julia.py
python julia.py  33.53s user 0.30s system 96% cpu 35.224 total


Just to be clear though, PyPy isn't always the best solution. It's great in this case though because you are repeatedly calling the same two functions over and over again, something that PyPy is generally much better than CPython at doing.

For a lot of, often considered, "slower" languages simply changing the implementation can do wonders.

* Well there are probably other things you can do, but switching the language is sooo easy, and helps with sooo much!