Even More Code Smells - Purge These 7 Python Putrid Peccadilloes Now!

Sdílet
Vložit
  • čas přidán 23. 07. 2024
  • Visit bit.ly/ARJAN50 to get 50% off the Pro version of Tabnine for 6 months.
    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. The code I worked on in this video is available here: github.com/ArjanCodes/2021-ev....
    Other code smells videos:
    - • 7 Python Code Smells: ...
    - • More Python Code Smell...
    💡 Here's my FREE 7-step guide to help you consistently design great software: arjancodes.com/designguide.
    🎓 Courses:
    The Software Designer Mindset: www.arjancodes.com/mindset
    The Software Designer Mindset Team Packages: www.arjancodes.com/sas
    The Software Architect Mindset: Pre-register now! www.arjancodes.com/architect
    Next Level Python: Become a Python Expert: www.arjancodes.com/next-level...
    The 30-Day Design Challenge: www.arjancodes.com/30ddc
    🛒 GEAR & RECOMMENDED BOOKS: kit.co/arjancodes.
    🚀If you want to take a quantum leap in your software development career, check out my course The Software Design Mindset: www.arjancodes.com/mindset.
    💬 Join my Discord server here: discord.arjan.codes
    🐦Twitter: / arjancodes
    🌍LinkedIn: / arjancodes
    🕵Facebook: / arjancodes
    👀 Channel code reviewer board:
    - Yoriz
    - Ryan Laursen
    - Sybren A. Stüvel
    - Dale Hagglund
    🔖 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
    DISCLAIMER - The links in this description might be affiliate links. If you purchase a product or service through one of those links, I may receive a small commission. There is no additional charge to you. Thanks for supporting my channel so I can continue to provide you with free content each week!

Komentáře • 173

  • @ArjanCodes
    @ArjanCodes  Před 2 lety +5

    Visit bit.ly/ARJAN50 to get 50% off the Pro version of Tabnine for 6 months!

  • @ehsisthatsweird
    @ehsisthatsweird Před 2 lety +20

    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.

  • @mikefochtman7164
    @mikefochtman7164 Před rokem +2

    @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

  • @XRay777
    @XRay777 Před 2 lety +33

    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.

  • @ChrisBNisbet
    @ChrisBNisbet Před 2 lety +51

    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.

    • @TheTacticalDood
      @TheTacticalDood Před 2 lety +2

      Indeed that is the practice in C since it has no other way of avoiding namespace collisions.

    • @cheebadigga4092
      @cheebadigga4092 Před 2 lety +2

      @@TheTacticalDood well it has structs though.

  • @pedromartindelcampogonzale9613

    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.

  • @artemhrytsenko1353
    @artemhrytsenko1353 Před 2 lety +5

    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).

  • @Claxiux
    @Claxiux Před 2 lety +5

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

    • @ZapOKill
      @ZapOKill Před 2 lety +1

      yeah, i am 100% sure he came up with the name all by himself!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +4

      Haha, I wish 😊. I think it was Martin Fowler who came up with the name.

  • @bocckoka
    @bocckoka Před 2 lety +2

    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

  • @imadetheuniverse4fun
    @imadetheuniverse4fun Před 2 lety +1

    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.

  • @joem8251
    @joem8251 Před 2 lety +25

    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!

  • @sergiovasquez7240
    @sergiovasquez7240 Před 2 lety +21

    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.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Thank you Sergio, will do!

    • @GulfCoastGrit
      @GulfCoastGrit Před 2 lety

      Agree completely! I use these to improve my C# skills! 😊

  • @shS83
    @shS83 Před 4 měsíci

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

    • @ArjanCodes
      @ArjanCodes  Před 4 měsíci +1

      Welcome aboard! Happy to have you here.

  • @programming_in_ukrainian
    @programming_in_ukrainian Před 2 lety +2

    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

  • @MichaelFJ1969
    @MichaelFJ1969 Před 2 lety

    Thanks for this video. It is really helpful. Please do consider making more such videos.

  • @timjohansson4304
    @timjohansson4304 Před 2 lety

    Hi Arjan! Great video as always, thanks for that. I would like to see a video describing your process of finding codesmells, this example is a really small code base to easily show and correct what you already found. Tips and tricks for modelling and finding these code smells would be much appreciated :)

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Thank you Tim! I do it on some level in the code roast / refactoring videos on this channel, but great topic suggestion to dive more into how to find them, thanks!

  • @saitaro
    @saitaro Před 2 lety

    Thanks again, mate, your videos really make me write better code!

  • @AlexBerkk
    @AlexBerkk Před 9 měsíci

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

  • @BrunoReisVideo
    @BrunoReisVideo Před 2 lety

    These videos are great. Feels like getting a brand new bottle of nice perfume

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Thanks so much Bruno, glad you liked it!

  • @Acuzzio
    @Acuzzio Před 2 lety

    This video is just excellent. Thanks a lot for providing such a good content!!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      You’re most welcome - I’m happy you like the content!

  • @marcosoliveira1538
    @marcosoliveira1538 Před 2 lety

    Great content. I would love to see a video about meta-programming, about your favorite decorators.

  • @NanaAtiekuFrans
    @NanaAtiekuFrans Před 2 lety

    Your contents are always great. Thanks.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Glad you like it, you’re welcome!

  • @an-lo9787
    @an-lo9787 Před 2 lety +9

    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!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +14

      Glad to hear it's helpful! It depends on the video type actually. For the code roast videos I work off the code of someone else, so there I mainly treat it as a code review and try to find areas of improvements. For these code smell videos, I have a list of things in my head that I've seen people make mistakes with in the past (including myself), and then I try to come up with an example that shows these things in a logical way.
      Overall, if I refactor code it's always an iterative process, where I try to improve something and then that triggers another possible improvement. Or what I had in mind didn't work and I have to change it back, haha :).

  • @aliemamalinezhad6523
    @aliemamalinezhad6523 Před 2 lety +1

    Excellent as always

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Thank you Ali, glad you liked the video!

  • @deatho0ne587
    @deatho0ne587 Před rokem

    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.

  • @kidsforcode3052
    @kidsforcode3052 Před 2 lety

    Thanks very much. Very well explained practical advice!

  • @zacky7862
    @zacky7862 Před 2 lety

    Another great tutorial :) Thank you so much

  • @DS-tj2tu
    @DS-tj2tu Před 2 lety

    Awesome! Thank you!

  • @kingremonoire239
    @kingremonoire239 Před 2 lety

    Hi Arjan. Love your content. please keep it up :)

  • @pypro
    @pypro Před 2 lety

    Amazing video, great bro!! =) many thanks

  • @Andrumen01
    @Andrumen01 Před 2 lety

    Just wanted to say that I like your channel very much. I am on a career change move and your tips have helped a lot to make the project I am currently working on be more organized.
    That being said, I was going to ask you about the implementation of Python events, how do you feel about code implementing events rather than direct calls to functions? Is there any "code smell" for implementing event systems?
    Best regards and keep up the excellent work.

  • @dariuszspiewak5624
    @dariuszspiewak5624 Před 2 lety

    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.

  • @xnick_uy
    @xnick_uy Před 2 lety +1

    Another excellent video. I think I got the majority of the main points. I'm not very experienced with async calls, and I'm also not too sure that I understand correctly the purpose of the @staticmethod qualifier. But now I have new learning goals!! :-)
    Please keep making these videos. Besides the content, the narrative and the edition is fantastic.

    • @XRay777
      @XRay777 Před 2 lety +1

      staticmethod (or classmethod for that matter) allows you to call a method directly on the type as opposed to an instance. Using this to create objects is actually also a design pattern. It usually goes by the name 'factory method'.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +2

      Glad it was helpful! I'm going to post a video about async soon!

    • @funkenjoyer
      @funkenjoyer Před 2 lety

      @@XRay777 Isn't usually used with @classmethod instead of @static? I guess both work just fine but I usually use the class one so that the method is decoupled from object but still tied to the class.

    • @XRay777
      @XRay777 Před 2 lety

      @@funkenjoyer yes, i agree. With instances you would more often see cloning behavior like implementing __deepcopy__ or some custom clone method of you need more control

  • @rdwells
    @rdwells Před 9 měsíci

    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.

  • @DanielWeikert
    @DanielWeikert Před rokem

    Hi found your channel and I really appreciate your videos.
    So far I always used python without paying much attention to structure and best practises (so 180 degree different to your style).
    I like to learn how to change that but I am not sure how to start the best way. Is there any order, guide,.. which helps to learn and apply your concepts and strategies the best way?
    Tahnks

  • @5ihdi
    @5ihdi Před rokem

    It would be interesting to see how we can implement Factory Design Pattern to create multiple Customer, Order etc in the main() function that manages to add this multiple instances to the POSSystem. And maybe even incorporate different PaymentProcessor classes

  • @erikdeveloper
    @erikdeveloper Před rokem

    Man, you're legend!

    • @ArjanCodes
      @ArjanCodes  Před rokem

      Thank you Erik, glad you liked the video!

  • @Gummibandet1
    @Gummibandet1 Před 2 lety +1

    Could you do a video showing what packages you use and recommend, as well as key commands you often use when coding?

  • @andyanderson222
    @andyanderson222 Před rokem

    Not only these tutorials are awesome, but i really like your sense of humor. These 7 x "Code smell" at the beginning was very funny)
    By the way, 1 question i have watching this video is why do we put default values on every Customer dataclass field? If we let user to create empty instance of customer without putting any info into constructor is it good for us? Like what will we do with this totally empty object later?

  • @adjbutler
    @adjbutler Před 2 lety

    thank you for this video

  • @DjegGoEu
    @DjegGoEu Před 2 lety +15

    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?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +12

      The main reason to use a class method is if you need to do something with the class. It would be a great solution in this case actually, because then you can call the initializer using cls and if you change the class name, you won't have to update this method. So yes, good point!

    • @frustbox
      @frustbox Před 2 lety +3

      I think in this case a classmethod would make a little more sense. It's strongly coupled to the object, in fact it's kind of a constructor. Some other examples are `datetime.now()`, `datetime.fromtimestamp()` or `datetime.strptime()` They are constructors that take some input to create a datetime object. In the code example here the `create()` method is a constructor (or initializer) that creates a PaymentProcessor object. So in this case, I would use classmethod, yes.
      IMHO staticmethods are more loosely coupled to the class. Kind of helper functions that are not really tied to the class or the individual object but may be used in conjunction with the class/object. And they are tacked onto the class as staticmethod solely to make imports easier. Otherwise in almost all situations I tend to avoid staticmethods and just use functions on the module/file level to not clutter the class with functions.
      But of course … that's all a bit convention and personal preference. There's no right or wrong here, I think.

  • @rdean150
    @rdean150 Před 2 lety

    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.

  • @theguy2887
    @theguy2887 Před 2 lety

    I love these videos!!!!

  • @SoChot
    @SoChot Před rokem

    Great video! In section 6, when you're defining the static method `create()` for the `StripePaymentProcessor` class, is there a reason you didn't use a class method since those are often used as object constructors?

  • @ZEDAWMN
    @ZEDAWMN Před 2 lety

    Thanks Arjan very helpful video.
    Can you make a video for the vscode extensions you are using for Python development ? I see you are using vscodevim and it will be interesting to see the rest.
    Thanks again

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Good idea, thanks for the suggestion!

  • @alexandersnow5867
    @alexandersnow5867 Před 2 lety

    Hey Arjan,
    Great video. I’ve been learning a lot watching this series. I was curious about your choice to make the create method for the StripPaymentProcessor a static method and not a class method? That would resolve the annotations. I’ve found myself to be a bit unclear on the benefits of either. I would assume a classmethod might be more handy if we wanted to make additional processors that inherit from StripPayment?

    • @akivazonenfeld7285
      @akivazonenfeld7285 Před 2 lety

      You can't do it with a class method since a class method is called on an instance of the class. And since this 'create' method supposes to be used to create the instance, it doesn't make sense to make it a class method.
      And I'm pretty sure It won't solve the typing problem.

    • @alexandersnow5867
      @alexandersnow5867 Před 2 lety

      @@akivazonenfeld7285 a class method is called on the class itself and usually returns an instance of the class, which seems to be what we’re doing here. It may not resolve the typing issue if we still say that the return is the StripePaymentProcessor though

    • @XRay777
      @XRay777 Před 2 lety

      I agree with you, Alexander. If you are implementeing the factory method pattern the default choice should be classmethod precisely for the reason you mentioned: it gets a reference to the type it is attached to as the first argument (usually denoted cls). If you derive from a class with a classmethod then the derived class will be passed as the cls argument if you call it on the derived class. Thid is the defining distinction between classmethod and staticmethod. A staticmethod does not get a reference to the instance / type it is attached to. It is mainly used to indicate that the method is logically connected to the class but not tightly coupled.

  • @robertbrummayer4908
    @robertbrummayer4908 Před 2 lety

    Another great video! The only thing I do not like and I guess also Uncle Bob would criticize are some redundant comments, e.g. the comments in the main method "create a customer", "create the order". As I have already pointed out in another comment, this is a code smell called redundant comments. Every programmer can see that you create a customer and order object there, so there is no need for these comments. There is also a doc string "Order status" that simply repeats what the name already tells. But this is just a minor point, great job!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Thanks Robert! You’re right. I was still on the fence here on what to do with code comments in my videos, as the default linter settings I used wanted to see comments everywhere. I’ve since turned that off and went with only adding comments if they contribute to the clarity of the code. I might do a video about comments soon to share my ideas about them.

    • @robertbrummayer4908
      @robertbrummayer4908 Před 2 lety

      @@ArjanCodes Such a video would be great. Thanks Arjan! Best wishes from Austria :)

  • @davidlakomski3919
    @davidlakomski3919 Před 2 lety

    Fortunately, my coding skills aren't advanced enough for my code to really stink xD Really love your videos and admire your teaching skills, thanks a lot !!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Don't worry, David, the smell develops all on its own regardless of your skill level ;).

    • @davidlakomski3919
      @davidlakomski3919 Před 2 lety

      Eager to discover my own putrid perfume mature then 😁

  • @Evkayne
    @Evkayne Před 2 lety

    I love those 10 seconds haha!

  • @finleycooper8844
    @finleycooper8844 Před 2 lety +1

    Make sure to add this to your code smell playlist so people can find it easier

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Done - thanks for pointing that out!

  • @alfonsov3190
    @alfonsov3190 Před 2 lety

    Thanks for the video! Especially for clarifying the problem with self-reference typing. Does the __future__ import also solve problems when there is a cyclic type reference between two or more classes?

    • @XRay777
      @XRay777 Před 2 lety +1

      Yes, it does and it also helps with cyclical imports. You can combine this with the TYPE_CHECKING flag from the typing modulec if you still want static type checking e.g. with mypy but want to avoid cyclical imports. In essence, you can then dump all the cyclical imports in a `if TYPE_CHECKING` block.

    • @alfonsov3190
      @alfonsov3190 Před 2 lety

      @@XRay777 Thanks a lot!

  • @tehdusto
    @tehdusto Před rokem

    As part of my job I'm having to move from python to LabVIEW. All of these practices are still applicable in the data flow paradigm and this content is helping me improve my companies LabVIEW code base.
    LabVIEW is cursed though

  • @puddlejumper1
    @puddlejumper1 Před 2 lety +1

    Love the code smell videos. Question: How can you reduce boiler plate code? For example you introduce 'LineItem' class but now you have to type it out 3 times in the main file (and potentially alot more if you have more items). Adding to the order.add_line_item function would reduce the code in main file. Thoughts?

    • @Milamber5186
      @Milamber5186 Před 2 lety +1

      In a typical pos system, all of your items would be in a database. You would not add items into the code, but instead add them to the database. In this video, it was added to the code for demonstration purposes.

    • @themadichib0d
      @themadichib0d Před 2 lety +1

      Doing so saves you some code lines in the short term for this example, but in the long term and/or real world use youd be making more work for yourself. And like the other person said, rarely are you constructing specific objects like this directly in code, and would be in taking from a source(eg database or a client) in which case youd never run into your specific concern in the first place.

  • @1oglop1
    @1oglop1 Před 2 lety

    I like the examples, however, I noticed that most of the videos work with data classes that are easy to change.
    What about refactoring an app that is using an ORM (Django or SQL Alchemy) - the pain going through the migrations (and data changes) during the refactoring is often well underestimated.
    I think that it would be nice if you can show the cost of changing the data model like that.
    Big plus point for saying `initializer` instead of constructor!

  • @TomWoodland
    @TomWoodland Před 2 lety +1

    You create a class variable "id", isn't that overwriting a built in/protected variable? Great video. Learnt a lot.

    • @XRay777
      @XRay777 Před 2 lety +1

      Yes, it is shadowing it, meaning that you cannot use the built-in id function in the scope of the class.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Indeed. I’m aware of the shadowing, but I never use that function and it’s a really useful variable name 😊. Alternatively you could name it _id, following the naming scheme used in NoSQL databases like MongoDB.

  • @potlurikrishnapriyatham

    any tool/ide extension to detect codesmells?

  • @allanmuller3486
    @allanmuller3486 Před 2 lety

    I may be getting ahead of myself, but mightn't it have been better to link the customer to the order using the id and keep the customer data in a dict keyed by id? Plusses include being able to maintain the customer id in one place, the minuses include not being able to having alternate shipping addresses for specific orders.

    • @XRay777
      @XRay777 Před 2 lety

      I have to say I don't really see a benefit to that. The way he did it you can easily get all ids by doing '[c.id for c in customers]' but grouping the customer data nicely from what you are suggesting is much more involved.

  • @javier232010gmail
    @javier232010gmail Před 2 lety

    I wonder if not is better to pass the Order dataclass to the payment method? From my point of view, probably at future, you need more information about it and you will need to add more params

  • @alessandroferrari2166
    @alessandroferrari2166 Před 2 lety

    what a gem of a video! Jam-packed with value, thank you Arjan! I was wondering: wouldn´t be better to have that staticmethod be a classmethod instead, since it returns a new instance. I'm a bit confused on the use of @classmethod in Python so my suggestion might be totally bs :D
    On another note, videos suggestions for the future: enterprise architecture patterns in Python, microservices in Python, DDD in Python. Again, thank you for your inspiring work!

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Hi Alessandro, thank you, glad you liked it! Yes indeed, this would be a great use case for a class method!

  • @slavrine
    @slavrine Před 2 lety

    Using wrong data structures vs backpedaling seems contradictory to each other. Should we be wrapping data in a class, and passing the instance around? This will allow us to avoid multiple lists around, but would make other code dependant on the names of methods and attributes of the data class. Thus by using a better data structure we introduce backpedaling. Thank you for great content Arjan!

  • @aadithyavarma
    @aadithyavarma Před 2 lety

    Great video! One question:
    class StripePaymentProcessor:
    def __init__(self):
    self.connected = False
    How do you change this into a dataclass? Should you use field(init=False, default=False) or use __post_init__() or both?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Thanks! You can change it into a dataclasses really easily:
      @dataclass
      class StripePaymentProcessor:
      connected: bool = False

    • @aadithyavarma
      @aadithyavarma Před 2 lety

      @@ArjanCodes Isn't the above dataclass equivalent to the below:
      class StripePaymentProcessor:
      def __init__(self, connected=False):
      self.connected = connected
      Since we do not want to initialize the value by passing it as an argument while creating the object, it should be avoided.
      1.
      @dataclass
      class StripePaymentProcessor:
      connected: bool = field(init=False, default=False)
      OR
      2.
      @dataclass
      class StripePaymentProcessor:
      connected: bool = field(init=False)
      def __post_init__(self):
      self.connected = False
      Which one do you think is better 1 or 2? Or is there a better approach?

  • @jurgen6706
    @jurgen6706 Před 2 lety +1

    I'm not familiar with Python's Protocol classes. But since there is no dependency between the PaymentProcessor protocol and the StripePaymentProcessor is it just assumed that when you pass the StripePaymentProcessor instance that it implements the protocol? Python being Python it probably will only actually throw an error at runtime, but I'm wondering what happens at write time. Does the type checking system check this when referencing a class that does not implement the protocol and will it give a warning if in this case the StripePaymentProcessor does not implement all the methods from the PaymentProcessor?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +2

      That’s indeed how it works. Check out my recent protocols vs ABC classes video where I show exactly what happens and at what point you get an error. When you’re writing code and you use a type checker like Pyright, it will point out protocol implementation inconsistencies.

  • @felipealvarez1982
    @felipealvarez1982 Před 2 lety

    I love your smelly and stinky videos Arjan! Thank you, now I'm going to go take a shower.

  • @pacersgo
    @pacersgo Před 2 lety

    I still don’t get at 23:03, why the “create” function is used instead of directly put the contents to __init__?

    • @kristik432
      @kristik432 Před 2 lety

      It's better to keep init clean czcams.com/video/1SHi1kriJI4/video.html

    • @pacersgo
      @pacersgo Před 2 lety +1

      @@kristik432 it makes sense. Thank you for the link!

  • @peterkuchar219
    @peterkuchar219 Před 2 lety

    I'm not sure customer should go as an input to Order. Shouldnt be other way around?

  • @guilhermearantes4627
    @guilhermearantes4627 Před 2 lety

    Where can I learn these topics in depth?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      I'm going to launch an in-depth course on software design before Christmas!

  • @dirk6531
    @dirk6531 Před 2 měsíci

    Your Order class ist decorated with @dataclass. Requestion: why contains the class functionality? Is it a good Idea to Split into Order class (only Data) and a OrderManager ( contains the business logic to handle Orders)?

  • @luukvdv5238
    @luukvdv5238 Před 2 lety

    What is your opinion about explicitly setting states from outside? I expected e.g. a pay() method that sets the state to paid instead of setting the state to paid explicitly.

    • @XRay777
      @XRay777 Před 2 lety

      Seems like something you would find in Java or C++. In python you can escalate access to attributes of you need to. If you only change the value of an attribute, leave it as an attribute. If at some point you need to restrict access or do some other stuff, like verification, just turn the attribute to a property with a setter. The interface for the user remains unchanged. You cannot do this in Java or C++, which is why programmers in those languages are so pedantic about getters and setters, since it is really hard to change an existing interface without breaking stuff.

    • @luukvdv5238
      @luukvdv5238 Před 2 lety

      It feels like leaking information of the implementation. From the outside you need to know the enum of those states.. also the state is already marked private using the _. So it's private and then set using a set method.

    • @XRay777
      @XRay777 Před 2 lety

      @@luukvdv5238 Fair points. Regarding the first, what I usually do is put all enums that are used throughout the project / package together in a file like common.py, which makes them easily accesable from everywhere. You can also expose them in the __init__.py of your package. I would not consider enums as implementation details, rather they are discretized options that can be used with the framework. To your second point, I missed the '_', but that only marks it protected. If you want to make it private you would need two '__' and then you can still have a read-only property with just 'status'. I don't really see the benefit in having a 'pay()' method that anyone can call over a property setter that restricts the status value which you can set. The latter gives you a simpler interface for the class.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      In general it's not a bad idea to separate implementation details like the order state from the users of the order. I can definitely imagine a pay() method as you suggest, or a set_to_paid() method. It becomes an issue though once there are many different values for the enum (cancelled, returned, refunded, payment_failed, etc.). In that case, adding a bunch of different setter methods for each of them leads to a cluttered Order class and it's better to expose the enum instead.

  • @gustavorangel729
    @gustavorangel729 Před 2 lety

    On 16:00, is there a particular reason to implement a property instead of a method like get_total_price(self)?

    • @funkenjoyer
      @funkenjoyer Před 2 lety

      it's pretty much a personal preference, they would behave pretty much the same, one thing tho that i've run into is that you can't rly create an abstract property so if you had a hierarchy of classes that all have get_total_price and you had an abstract base where it's defined as abstract method you couldn't rly swap it for property

    • @XRay777
      @XRay777 Před 2 lety +2

      It is considered more pythonic. You can think of the total price as just a value that is automatically updated. Therefore it makes sense to access it just like any other attribute, hence the property. It allows you to skip the empty parentheses when calling it as opposed to a method.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Exactly what @X Ray says!

    • @XRay777
      @XRay777 Před 2 lety

      @@uqams I disagree somewhat with that statement. The whole point of properties (or the whole descriptor protocol for that matter) is the flexibility to change access without changing the interface. If you would never use properties if computation is involved the whole concept would be moot. On the other hand, when the computation has side effects, as in changing something about the state of the class other than the attribute you are setting, or if the computation can be costly, then you should definitely go for a method instead.

    • @kuishikama348
      @kuishikama348 Před 2 lety

      @@XRay777 So it works all very much the same, but you would use properties when you don't change the state of the object, basically when "not much" is happening and you basically just want to return a saved value or derive something directly from the saved values (like the totalprice = quantity * price)? You would use a method, indicated with the () if you want to describe that something is changing, like a create(), run(), start(), close() and so on? I think this is a TIL moment for me. Thanks for that.
      I just wonder what happens if you want to determine the totalprice, but it involves a costly function, something like accessing a web-resource, lots of calculations, things like that? Do you still use a totalprice property or do you use a method get_totalprice() to indicate to someone reading the code that this is costly? I just imagine someone extending or maintaining the code later on, not realizing the costs of these simple looking properties and then wondering why it is so slow. Profiling and debugging until realising that obj.totalprice is actually generating requests, starting a long calculation and so on. Would you use properties or methods for that?

  • @getpoked101
    @getpoked101 Před 2 lety

    I know you are using the vim extension but you still use your most quite a bit to navigate. Is that just for demonstration or do you have a method to the madness ?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      That’s mostly me having to unlearn 30 years of text editing with a mouse. I’m slowly adopting more Vim-like practices though, so I’ll hopefully get better at it soon.

    • @getpoked101
      @getpoked101 Před 2 lety

      @@ArjanCodes I'm at about 15 years of it. It's odd I learned on emacs, then as I become a professional I used a bunch of mouse driven IDE's and now I'm in the same boat as you. I find my biggest issue with the vim extension is when it plays poorly with other extensions.

  • @polyliker8065
    @polyliker8065 Před 2 lety +1

    Hey Arjan, I've recently been in an introductory course as part of a new job where we had to work with a system where stored procedures ruled the game and handled everything from data access to business logic. I asked the instructor why there was business logic in the database when we're already doing business logic in the visual basic app.
    He kinda got upset and said he didn't hear a good argument why it shouldn't be there, and only said that stored procedures are safer but less portable.
    I didn't really agree on that but kept my mouth shut since I didn't want to cause a scene. (I can think of a few reasons why stored procedures aren't considered best practices.)
    Do you have a view on the pro's and con's of stored procedures, in which case they are usefull and when they should be avoided?

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      I avoid using stored procedures in my projects because I like to have my business logic in a single place. Having it in multiple places leads to more work in the future: if you need to change something, you’ll probably need to change it in multiple places. Also, you now have an extra decision to make: should this new thing be a stored procedure or in the backend code? If you make the wrong choice, it costs time to fix it. Finally, having two places where business logic is stored might limit you in how you setup your software architecture, leading to a potentially less optimal solution.

    • @andreacazzaniga8488
      @andreacazzaniga8488 Před 2 lety +3

      You might be true, but that's not very relevant. (disclaimer: not requested advice) many business implementations are far from ideal, and this is not even a case where the implementation is necessarily bad. Wait until having a stored procedure gives a problem and be the one who knows how to solve the issue. If the stored procedure works and has never given a problem.. Keep it like this, even if it could be better. You will always want to change the cod base and stuff of projects, but.. Focus on fixing stuff that don't work or that you are asked to change. The rest.. Wait until you are more senior and your colleagues know you and trust you.

    • @polyliker8065
      @polyliker8065 Před 2 lety

      @@ArjanCodes Thanks for the reply!
      Those were indeed some of the reasons it seemed not to be a best practice to me. I didn't think about the limitation in terms of architecture though. Another was having two code bases to maintain and deploy potentially causing synchronisation issues which might introduce errors.

    • @polyliker8065
      @polyliker8065 Před 2 lety

      ​@@andreacazzaniga8488 Yeah, it was not meant as a correctional comment but more of a question on why we've gotten this as a sample app. I mean it'd be nice to be introduced to stored procedures without the sample app actually using them. So we get more of a message along the lines of 'while we'd rather put our business logic in one place, but you might encounter this. Just don't touch it unless asked'.
      And don't worry, the focus is always on what is asked. Don't be afraid to ask why something is done a certain way when you see possible improvement though.

  • @andymachine6545
    @andymachine6545 Před 2 lety

    What's the code smell related to the thumbnail? Cause thats what my code typically looks like :D

  • @stefanobailey740
    @stefanobailey740 Před 2 lety

    Can someone explain to me why you wouldn't use a Dataclass over a regular class? I understand that you can't use the dunder method new, but with slots being added to 3.10 that can't be a factor. If anyone could clear this up, I would really appreciate it

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      Sometimes your class is not at all data-oriented, and has mostly methods and not many instance variables. In that case you might als want to have more control over the initializer instead of what a dataclass provides by default. For example, a ExportFactory might not have any instance variables, but simply have as a task to create a variety of Exporters. In that case, turning it into a dataclass doesn’t add any value.

    • @stefanobailey740
      @stefanobailey740 Před 2 lety

      @@ArjanCodes thank you so much for your response. I really appreciate ypu taking the time to clear it up for me

  • @mypegionworld7612
    @mypegionworld7612 Před 2 lety

    You are the only person that makes me insecure about my code..

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      No need! We’re all here to learn (including myself).

  • @ChrisBNisbet
    @ChrisBNisbet Před 2 lety

    Given the number of times you use the word 'interface' when describing protocol classes, I wonder if a better name for Protocol would have been Interface.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      I would have liked a more strict interface-like programming concept in Python, like Java or Typescript has. But protocols are still quite nice and tie in well with Python’s typing system.

  • @TomWoodland
    @TomWoodland Před 2 lety

    Code smells!!!

  • @rrwoodyt
    @rrwoodyt Před 2 lety

    I still miss the whteboard!

  • @amir3515
    @amir3515 Před 2 lety +1

    Come on, it's only smellz 😉

  • @Kahitar1
    @Kahitar1 Před 2 lety

    Why don't you ever use multiple cursors? For example you could have selected all "add_line_item"s and changed them all at once to pass LineItem. Just curious if you don't know about multiple cursors or have another reason to never use them :)
    Also just want to say that I love your content. I learn a lot from you in every video and it already improved my python code A TON! Thank you :)
    ALSO: I noticed you are using pyenv. I never used that, I always used venv or pipenv. As a suggestion for a video: I would love a pyenv Tutorial from you. Of course you don't have to do it just for me, I can just watch other tutorials. Just if you think it fits to the content of your channel ^^

    • @ArjanCodes
      @ArjanCodes  Před 2 lety +1

      The main reason I avoid them in my videos is that I'm concerned it would make the explanations less clear since text is changing in multiple places at the same time. I do use name refactoring a lot (F2 in VSCode).
      Glad to hear the videos are helpful to you. Regarding pyenv, good suggestion, thanks!

  • @aclassypotato8155
    @aclassypotato8155 Před 2 lety

    hi arjan

  • @williamwatkins6669
    @williamwatkins6669 Před rokem

    regarding the last code smell. I actually learnt from mcodes (czcams.com/video/R8-oAqCgHag/video.html) that you can force the user of a library to use keywords by using the * in the arguments. Can be handy

  • @originalvbgunz
    @originalvbgunz Před 2 lety

    These videos are cool but smaller/quicker videos *focusing* on single subjects may be a better idea. Think seven videos each focused on a single subject, best practice, idea, skill Vs 1 video of seven subjects in which it is easy to get lost, confused, bored, stuck and forget altogether. Your videos are great and better than nothing (thank you for them) but focused videos might be a better idea

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      I agree to a certain point - I'm actually working on focusing my videos more at the moment. It's a balance though between focusing on a single thing but still allowing for enough depth. Sometimes, too much focus can lead to content with too little informational value. And I'd like to avoid that :).

  • @ChazAllenUK
    @ChazAllenUK Před 2 lety

    "So here's a warning..."
    ROFL

  • @abdulahhi
    @abdulahhi Před 2 lety

    goats milk?

  • @Manas-co8wl
    @Manas-co8wl Před rokem

    Lol people complaining about the word "code smell" as if he invented it when it was a programming jargon since the stone ages (when code really did smell btw..)

  • @PeriOfTheGee
    @PeriOfTheGee Před 2 lety

    You used named arguments for all 3 `LineItem`s, but shouldn't doing it just for 1 be enough to let the developer know which argument means what? Of course, as a devils advocate, I can say that they could all possibly be in a different order than in the ctor, but no sane person would do that while still indicating only one of them with named arguments.

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      It might be enough but it’s also hard to do this consistently because any argument following a named argument also has to be named. So you can only do this if you want to clarify it for the last few arguments, in which case you can keep the first ones unnamed. Overall, I tend to use unnamed arguments for simpler functions with one or two arguments, and named arguments for anything else, or when it’s a function with arguments of the same type.

  • @grimonce
    @grimonce Před 2 lety

    Great explanations and implementation, I however don't like the tabnine as a sponsor :/
    I've heard some worrying things about tabnine and tried it myself, quite bloated. Tabnine is not open about its methods and there really is no reason to trust it.
    It is advertised however and people will start asking you to use it in your professional environment - bad influence on our community...

    • @ArjanCodes
      @ArjanCodes  Před 2 lety

      Thanks! It's been a while since I did this video and I thought Tabnine was a nice tool at the time I recorded this, but perhaps things have changed since then. I don't think Tabnine is less open about their methods than other companies though, or am I missing something?