First of all, let's split your image into 4 parts, and make them all the same size. That will make our task easier:
head.png
body.png
L.png
tail.png
Let's load them using a basic pygame game:
import pygame
TILESIZE = 24
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
load = lambda part: pygame.image.load(part + '.png').convert_alpha()
parts = ('head', 'body', 'tail', 'L')
head_img, body_img, tail_img, L_img = [load(p) for p in parts]
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill((30, 30, 30))
screen.blit(head_img, (100, 100))
screen.blit(body_img, (100, 100 + TILESIZE))
screen.blit(L_img, (100, 100 + TILESIZE*2))
screen.blit(tail_img, (100, 100 + TILESIZE*3))
dt = clock.tick(60)
pygame.display.flip()
main()

But we actually also need these images in a rotated form, so let's create them at the beginning:
def build_images():
load = lambda part: pygame.image.load(part + '.png').convert_alpha()
parts = ('head', 'body', 'tail', 'L')
head_img, body_img, tail_img, L_img = [load(p) for p in parts]
return {
'HEAD_N': head_img,
'HEAD_S': pygame.transform.rotate(head_img, 180),
'HEAD_E': pygame.transform.rotate(head_img, 90),
'HEAD_W': pygame.transform.rotate(head_img, -90),
'BODY_NN': body_img,
'BODY_SS': body_img,
'BODY_WW': pygame.transform.rotate(body_img, 90),
'BODY_EE': pygame.transform.rotate(body_img, 90),
'BODY_NE': pygame.transform.rotate(L_img, 180),
'BODY_WS': pygame.transform.rotate(L_img, 180),
'BODY_WN': pygame.transform.rotate(L_img, 90),
'BODY_SE': pygame.transform.rotate(L_img, 90),
'BODY_ES': pygame.transform.rotate(L_img, -90),
'BODY_NW': pygame.transform.rotate(L_img, -90),
'BODY_EN': pygame.transform.rotate(L_img, 0),
'BODY_SW': pygame.transform.rotate(L_img, 0),
'TAIL_N': tail_img,
'TAIL_S': pygame.transform.rotate(tail_img, 180),
'TAIL_E': pygame.transform.rotate(tail_img, 90),
'TAIL_W': pygame.transform.rotate(tail_img, -90)
}
Using a dictionary with string keys will make it easy for us to get the correct image depending on the direction of each part of the snake and their parent part.
For example, BODY_SE is the image we use when the part of the snake faces east, but the parent is going to move south.
Now we can start implementing our game. Since we're using pygame I'll use basic pygame features like Sprite and Group. Let's see how we could create some Sprites representing the snake:
import pygame
TILESIZE = 24
class Snake(pygame.sprite.Sprite):
images = None
def __init__(self, grp, pos, length, parent=None):
super().__init__(grp)
self.parent = parent
self.child = None
if not self.parent:
self.image = Snake.images['HEAD_N']
elif length == 1:
self.image = Snake.images['TAIL_N']
else:
self.image = Snake.images['BODY_NN']
self.pos = pos
self.rect = self.image.get_rect(x=pos[0]*TILESIZE, y=pos[1]*TILESIZE)
if length > 1:
self.child = Snake(grp, (pos[0], pos[1]+1), length-1, self)
def build_images():
...
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
Snake.images = build_images()
all_sprites = pygame.sprite.Group()
snake = Snake(all_sprites, (4, 4), 6)
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill((30, 30, 30))
all_sprites.update()
all_sprites.draw(screen)
dt = clock.tick(60)
pygame.display.flip()
main()
As you can see, each part of the snake has a reference to the part before (except for the head part), and a refence to the part behind (except for the tail).
So far, so good. Let's move the snake:
import pygame
TILESIZE = 24
class Snake(pygame.sprite.Sprite):
images = None
def __init__(self, grp, pos, length, parent=None):
...
def move(self):
# if we have a parent, let's look were it moves
parent_direction = self.parent.direction if self.parent else None
if self.direction == 'N': self.pos = self.pos[0], self.pos[1] - 1
elif self.direction == 'S': self.pos = self.pos[0], self.pos[1] + 1
elif self.direction == 'E': self.pos = self.pos[0] - 1, self.pos[1]
elif self.direction == 'W': self.pos = self.pos[0] + 1, self.pos[1]
self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)
# move the child
if self.child:
self.child.move()
# follow the parent
if parent_direction:
self.direction = parent_direction
def update(self):
# no parent means we're the head of the snake
# and we should move we a key is pressed
if not self.parent:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_w]: self.direction = 'N'
if pressed[pygame.K_s]: self.direction = 'S'
if pressed[pygame.K_a]: self.direction = 'E'
if pressed[pygame.K_d]: self.direction = 'W'
def main():
...
# let's trigger the MOVE event every 500ms
MOVE = pygame.USEREVENT + 1
pygame.time.set_timer(MOVE, 500)
...
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == MOVE:
snake.move()

Wonderfull. What's left is to actually change the image of each body part if the direction changes.
Here's the full code:
import pygame
TILESIZE = 24
class Snake(pygame.sprite.Sprite):
images = None
def __init__(self, grp, pos, length, parent=None):
super().__init__(grp)
self.parent = parent
self.child = None
self.direction = 'N'
if not self.parent: self.image = Snake.images['HEAD_N']
elif length == 1: self.image = Snake.images['TAIL_N']
else: self.image = Snake.images['BODY_NN']
self.pos = pos
self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)
if length > 1:
self.child = Snake(grp, (pos[0], pos[1]+1), length-1, self)
def move(self):
# if we have a parent, let's look were it moves
parent_direction = self.parent.direction if self.parent else None
if self.direction == 'N': self.pos = self.pos[0], self.pos[1] - 1
elif self.direction == 'S': self.pos = self.pos[0], self.pos[1] + 1
elif self.direction == 'E': self.pos = self.pos[0] - 1, self.pos[1]
elif self.direction == 'W': self.pos = self.pos[0] + 1, self.pos[1]
self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)
# move the child
if self.child:
self.child.move()
if not self.parent: self.image = Snake.images['HEAD_' + self.direction]
elif not self.child: self.image = Snake.images['TAIL_' + parent_direction]
else: self.image = Snake.images['BODY_' + parent_direction + self.direction]
# follow the parent
if parent_direction:
self.direction = parent_direction
def update(self):
# no parent means we're the head of the snake
# and we should move we a key is pressed
if not self.parent:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_w]: self.direction = 'N'
if pressed[pygame.K_s]: self.direction = 'S'
if pressed[pygame.K_a]: self.direction = 'E'
if pressed[pygame.K_d]: self.direction = 'W'
def build_images():
load = lambda part: pygame.image.load(part + '.png').convert_alpha()
parts = ('head', 'body', 'tail', 'L')
head_img, body_img, tail_img, L_img = [load(p) for p in parts]
return {
'HEAD_N': head_img,
'HEAD_S': pygame.transform.rotate(head_img, 180),
'HEAD_E': pygame.transform.rotate(head_img, 90),
'HEAD_W': pygame.transform.rotate(head_img, -90),
'BODY_NN': body_img,
'BODY_SS': body_img,
'BODY_WW': pygame.transform.rotate(body_img, 90),
'BODY_EE': pygame.transform.rotate(body_img, 90),
'BODY_NE': pygame.transform.rotate(L_img, 180),
'BODY_WS': pygame.transform.rotate(L_img, 180),
'BODY_WN': pygame.transform.rotate(L_img, 90),
'BODY_SE': pygame.transform.rotate(L_img, 90),
'BODY_ES': pygame.transform.rotate(L_img, -90),
'BODY_NW': pygame.transform.rotate(L_img, -90),
'BODY_EN': pygame.transform.rotate(L_img, 0),
'BODY_SW': pygame.transform.rotate(L_img, 0),
'TAIL_N': tail_img,
'TAIL_S': pygame.transform.rotate(tail_img, 180),
'TAIL_E': pygame.transform.rotate(tail_img, 90),
'TAIL_W': pygame.transform.rotate(tail_img, -90)
}
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
Snake.images = build_images()
# let's trigger the MOVE event every 500ms
MOVE = pygame.USEREVENT + 1
pygame.time.set_timer(MOVE, 500)
all_sprites = pygame.sprite.Group()
snake = Snake(all_sprites, (4, 4), 8)
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == MOVE:
snake.move()
screen.fill((30, 30, 30))
all_sprites.update()
all_sprites.draw(screen)
dt = clock.tick(60)
pygame.display.flip()
main()
