Purge These 7 Code Smells From Your Python Code

preview_player
Показать описание

Yes, I'm back with 7 more code smells for you! I describe each smell using an example in Python and then show you how to fix it.

🔖 Chapters:
0:00 Intro
1:29 Explaining the example
6:02 About code smells
6:36 #1: using the wrong data structure
9:23 #2: using misleading names
10:54 #3: classes with too many instance variables
14:23 #4: Verb/subject
18:03 #5: Backpedalling
21:32 #6: Hard-wired sequences with a fixed order
24:31 #7: Creating unrelated objects in the initializer
27:04 BONUS: Not relying on keyword arguments

#arjancodes #softwaredesign #python
Рекомендации по теме
Комментарии
Автор

@12:25 The little flip back and forth to confirm your customer ID was an integer, reminded me of a huge argument / debate our team had once. One side was, "It's made up of digits, so it's an 'int'." Other side was, "You do NOT perform any math or calculations with it so it is NOT an integer, it's a string of digits." (and a single individual that voted for, "It's a unique identifier, so it's its own class.")

Couple of boring meetings later, the 'string of digits' camp finally wore everyone down and won the debate. lol

mikefochtman
Автор

python docs prefer raise NotImplementedError to ‘pass’ or ellipsis in empty abstract/protocol methods. I guess the reasoning for this is if you use super() then ‘pass’ might get you into trouble.

ehsisthatsweird
Автор

A small side note on the annotation matter:
'from __future__ import annotations' is only available from python 3.7+. If you are on 3.6 you can just turn the annotation into a string, which will delay the evaluation until after the class is defined (see PEP 563 for details). The import actually does the same thing, but automagically.

XRay
Автор

Code smells is an excellent name. It’s what made this series stand out and your channel with it!

Claxiux
Автор

Ha! Finally found somebody who does not teach Python basics and the dry stuff of how to loop over a list (it's important as well, don't get me wrong), but actually teaches about Python software architecture and best practices :) Thank you, Arjan. That's genuinely valuable. By the way, Arjan, if you want really, really, REALLY awesome programmer keyboard and heavenly typing experience, get yourself the Kinesis Advantage keyboard. Once you do it, you'll never, ever look back on the usual QWERTY crap (even the so-called "ergonomic") which I don't touch today and don't want to even look at. I've also re-trained myself to type on a modified Dvorak layout (called Dvorak Programmer), so my hands and wrists will be healthy and thankful to me for the rest of my programming life :))) Not to mention the speed of touch-typing. Great videos. Gonna subscribe and have a look at your courses.

dariuszspiewak
Автор

I am not a Python programmer, but I feel this an amazing resource to start good coding practices as soon as possible.
Most of this advices translate very well to programming in general.
Keep the hard work, and I hope there are more of this kind of videos coming. I bet there are very hard to make.

sergiovasquez
Автор

I think you did a really good job showing code smells in code that may pass as "good" at first glance. For example the payment processor having a Protocol at first glance seemed like a good thing, but it turns out you don't need to use that and can decouple even further by simply creating the payment function with the correct parameters.

Really shows that you should really think about how and where you apply these design patterns and principles.

imadetheuniversefun
Автор

In the last code smell, the one about keyword args, if you are programming on a language that doesn't have this feature, you can use the builder design pattern when you have too many arguments for a constructor. If you're calling a method that is not a constructor, first, your method may be doing too much, but if you really need all the arguments, you can wrap them in a dataclass if it makes sense. Maybe you can even move the whole method and even others to this new class, if it feels better.

pedromartindelcampogonzale
Автор

27:05 you can put * at def some_function(*, a, b): ... and using it every single argument have to be assigned with keyword arguments (otherwise, TypeError would throw).

artemhrytsenko
Автор

to 6: in Rust, where you have move semantics, you can guarantee at compile time that you use your objects with stages properly, meaning that a state transition 'moves' the object, and you can't call methods of the previous state on it anymore, because it doesn't exist. it's called typestate pattern

bocckoka
Автор

To your bonus tip, I agree in general.
For most IDEs or Code Editors worth using, you can hover over the function/method name and get the names of every parameter/argument and their respective types. Also should give different impementations of a function if their are mutliple versions.

VSC which it seems like you are using even did this around 27:59. Meaning a dev does not really have to do it if they know how to use their respective editor.

deathone
Автор

The common prefix "customer_" is a hint to the dev/reviewer that the variables could/should be moved into some separate class/structure.
I often see something similar in 'C' code where people create groups of structure fields all starting with the same prefix.

ChrisBNisbet
Автор

Hey Arjan! Nice video, thank you, but I have two things to discuss:
1. I slightly disagree with add_line_item implementation. IMHO it's better to pass native data types (name: str, quantity: int, price: int) than some custom type 'cause at least you can simplify communication with outer code (unpack dict or they would call method by passing native types) and not obligate other people to use your own object to do so.
2. Importing annotations from future is not the only way to declare your method would return value of the class itself, you can wrap its name in quotes and type hinting would still work the same way

class A:
def method(self) -> 'A':
pass

programming_in_ukrainian
Автор

Watched your videos for the first time yesterday and became an instant fan. Subscribed. Keep up the good work!

shS
Автор

Tabnine ad. Pre-Copilot days were wild. I'm not crying, you're crying!

AlexBerkk
Автор

I would argue that there is one more minor code smell here: using an integer for a customer ID. My rule of thumb is that if you are not doing arithmetic on it, it's not a number.

I learned this hard way with some manufacturing software I wrote. It had to read a list of order numbers in a text file dropped by some IT system and process those orders. They were 12-digit numbers, so I stored them as ints. (This was C++, so they were 64-bit ints.) Then IT decided to slowly replace their old mainframe database with a new Oracle system, and decided to mark orders from the new system by replacing the 12th digit with an "E". They did this without warning. I had one day to modify the program and change the type of a primary key on a bunch of database tables.

rdwells
Автор

I prefer "code aroma". It really rolls off the tongue more nicely. Because, as we all know, mouthfeel is a crucial aspect of software design patterns.

rdean
Автор

Hey Arjan! I appreciate you and the content you post weekly is very helpful. I have one question, do you import these codes and then determine areas you could improve, or do you write these codes and are going back over them now having a different perspective? Thank you for your feedback in advance!

an-lo
Автор

You do a lot of good videos that illustrate SOLID principles. I think if you expand on those concepts with automatically-generated UML diagrams with Pylint's Pyreverse you could create some interesting content! Thank you for your videos -- you somehow always have something I need -- occasionally just when I need it!

joem
Автор

Hey Arjan,

At minute 22:56, doesn't it make more sense to use a class method to handle the creation and deployment of the object? I always have a hard time distinguishing between class method and static method usages, but that case seems to work fine with class methods.

What do you think?

DjegGoEu