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

Поділитися
Вставка
  • Опубліковано 19 чер 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!

КОМЕНТАРІ • 173

  • @ArjanCodes
    @ArjanCodes  2 роки тому +5

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

  • @ehsisthatsweird
    @ehsisthatsweird 2 роки тому +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 Рік тому +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

  • @ChrisBNisbet
    @ChrisBNisbet 2 роки тому +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 2 роки тому +2

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

    • @cheebadigga4092
      @cheebadigga4092 2 роки тому +2

      @@TheTacticalDood well it has structs though.

  • @XRay777
    @XRay777 2 роки тому +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.

  • @artemhrytsenko1353
    @artemhrytsenko1353 2 роки тому +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 2 роки тому +5

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

    • @ZapOKill
      @ZapOKill 2 роки тому +1

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому +4

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

  • @sergiovasquez7240
    @sergiovasquez7240 2 роки тому +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  2 роки тому +1

      Thank you Sergio, will do!

    • @GulfCoastGrit
      @GulfCoastGrit 2 роки тому

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

  • @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.

  • @imadetheuniverse4fun
    @imadetheuniverse4fun 2 роки тому +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.

  • @bocckoka
    @bocckoka 2 роки тому +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

  • @joem8251
    @joem8251 2 роки тому +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!

    • @ArjanCodes
      @ArjanCodes  2 роки тому +1

      Thanks Joe, will look into it!

  • @programming_in_ukrainian
    @programming_in_ukrainian 2 роки тому +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

  • @shS83
    @shS83 3 місяці тому

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

    • @ArjanCodes
      @ArjanCodes  3 місяці тому +1

      Welcome aboard! Happy to have you here.

  • @MichaelFJ1969
    @MichaelFJ1969 2 роки тому

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

  • @AlexBerkk
    @AlexBerkk 8 місяців тому

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

  • @timjohansson4304
    @timjohansson4304 2 роки тому

    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  2 роки тому

      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!

  • @BrunoReisVideo
    @BrunoReisVideo Рік тому

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

    • @ArjanCodes
      @ArjanCodes  Рік тому

      Thanks so much Bruno, glad you liked it!

  • @saitaro
    @saitaro 2 роки тому

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

  • @an-lo9787
    @an-lo9787 2 роки тому +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  2 роки тому +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 :).

  • @marcosoliveira1538
    @marcosoliveira1538 2 роки тому

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

  • @zacky7862
    @zacky7862 2 роки тому

    Another great tutorial :) Thank you so much

    • @ArjanCodes
      @ArjanCodes  2 роки тому

      Glad you liked it, @Zacky!

  • @kidsforcode3052
    @kidsforcode3052 2 роки тому

    Thanks very much. Very well explained practical advice!

  • @NanaAtiekuFrans
    @NanaAtiekuFrans 2 роки тому

    Your contents are always great. Thanks.

    • @ArjanCodes
      @ArjanCodes  2 роки тому

      Glad you like it, you’re welcome!

  • @Acuzzio
    @Acuzzio 2 роки тому

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому

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

  • @deatho0ne587
    @deatho0ne587 Рік тому

    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.

  • @Andrumen01
    @Andrumen01 2 роки тому

    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.

  • @DanielWeikert
    @DanielWeikert Рік тому

    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

  • @aliemamalinezhad6523
    @aliemamalinezhad6523 Рік тому +1

    Excellent as always

    • @ArjanCodes
      @ArjanCodes  Рік тому

      Thank you Ali, glad you liked the video!

  • @DS-tj2tu
    @DS-tj2tu 2 роки тому

    Awesome! Thank you!

  • @pypro
    @pypro 2 роки тому

    Amazing video, great bro!! =) many thanks

  • @DjegGoEu
    @DjegGoEu 2 роки тому +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  2 роки тому +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 2 роки тому +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.

  • @kingremonoire239
    @kingremonoire239 2 роки тому

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

  • @5ihdi
    @5ihdi Рік тому

    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

  • @dariuszspiewak5624
    @dariuszspiewak5624 2 роки тому

    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.

  • @SoChot
    @SoChot Рік тому

    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?

  • @Gummibandet1
    @Gummibandet1 2 роки тому +1

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому

      Thanks for the suggestion!

  • @rdwells
    @rdwells 8 місяців тому

    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.

  • @andyanderson222
    @andyanderson222 Рік тому

    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?

  • @rdean150
    @rdean150 2 роки тому

    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.

  • @xnick_uy
    @xnick_uy 2 роки тому +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 2 роки тому +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  2 роки тому +2

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

    • @funkenjoyer
      @funkenjoyer 2 роки тому

      @@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 2 роки тому

      @@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

  • @erikdeveloper
    @erikdeveloper Рік тому

    Man, you're legend!

    • @ArjanCodes
      @ArjanCodes  Рік тому

      Thank you Erik, glad you liked the video!

  • @adjbutler
    @adjbutler 2 роки тому

    thank you for this video

  • @ZEDAWMN
    @ZEDAWMN 2 роки тому

    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  2 роки тому

      Good idea, thanks for the suggestion!

  • @theguy2887
    @theguy2887 2 роки тому

    I love these videos!!!!

    • @ArjanCodes
      @ArjanCodes  2 роки тому

      Thanks, glad you like them!

  • @alexandersnow5867
    @alexandersnow5867 2 роки тому

    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 2 роки тому

      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 2 роки тому

      @@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 2 роки тому

      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.

  • @puddlejumper1
    @puddlejumper1 2 роки тому +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 2 роки тому +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 2 роки тому +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.

  • @tehdusto
    @tehdusto Рік тому

    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

  • @alfonsov3190
    @alfonsov3190 2 роки тому

    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 2 роки тому +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 2 роки тому

      @@XRay777 Thanks a lot!

  • @finleycooper8844
    @finleycooper8844 2 роки тому +1

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому

      Done - thanks for pointing that out!

  • @1oglop1
    @1oglop1 2 роки тому

    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!

  • @Evkayne
    @Evkayne 2 роки тому

    I love those 10 seconds haha!

  • @TomWoodland
    @TomWoodland 2 роки тому +1

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

    • @XRay777
      @XRay777 2 роки тому +1

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому +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.

  • @slavrine
    @slavrine Рік тому

    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!

  • @robertbrummayer4908
    @robertbrummayer4908 2 роки тому

    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  2 роки тому +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 2 роки тому

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

  • @davidlakomski3919
    @davidlakomski3919 2 роки тому

    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  2 роки тому +1

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

    • @davidlakomski3919
      @davidlakomski3919 2 роки тому

      Eager to discover my own putrid perfume mature then 😁

  • @allanmuller3486
    @allanmuller3486 2 роки тому

    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 2 роки тому

      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.

  • @jurgen6706
    @jurgen6706 2 роки тому +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  2 роки тому +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.

  • @aadithyavarma
    @aadithyavarma 2 роки тому

    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  2 роки тому

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

    • @aadithyavarma
      @aadithyavarma 2 роки тому

      @@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?

  • @javier232010gmail
    @javier232010gmail 2 роки тому

    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

  • @potlurikrishnapriyatham
    @potlurikrishnapriyatham 11 місяців тому

    any tool/ide extension to detect codesmells?

  • @alessandroferrari2166
    @alessandroferrari2166 2 роки тому

    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  2 роки тому +1

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

  • @polyliker8065
    @polyliker8065 2 роки тому +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  2 роки тому +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 2 роки тому +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 2 роки тому

      @@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 2 роки тому

      ​@@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.

  • @getpoked101
    @getpoked101 2 роки тому

    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  2 роки тому +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 2 роки тому

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

  • @guilhermearantes4627
    @guilhermearantes4627 2 роки тому

    Where can I learn these topics in depth?

    • @ArjanCodes
      @ArjanCodes  2 роки тому +1

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

  • @peterkuchar219
    @peterkuchar219 2 роки тому

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

  • @dirk6531
    @dirk6531 27 днів тому

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

  • @mypegionworld7612
    @mypegionworld7612 2 роки тому

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

    • @ArjanCodes
      @ArjanCodes  2 роки тому +1

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

  • @luukvdv5238
    @luukvdv5238 2 роки тому

    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 2 роки тому

      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 2 роки тому

      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 2 роки тому

      @@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  2 роки тому

      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.

  • @andymachine6545
    @andymachine6545 2 роки тому

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

  • @felipealvarez1982
    @felipealvarez1982 2 роки тому

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

  • @gustavorangel729
    @gustavorangel729 2 роки тому

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

    • @funkenjoyer
      @funkenjoyer 2 роки тому

      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 2 роки тому +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  2 роки тому +1

      Exactly what @X Ray says!

    • @XRay777
      @XRay777 2 роки тому

      @@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 2 роки тому

      @@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?

  • @pacersgo
    @pacersgo 2 роки тому

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

    • @kristik432
      @kristik432 2 роки тому

      It's better to keep init clean ua-cam.com/video/1SHi1kriJI4/v-deo.html

    • @pacersgo
      @pacersgo 2 роки тому +1

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

  • @TomWoodland
    @TomWoodland 2 роки тому

    Code smells!!!

  • @rrwoodyt
    @rrwoodyt 2 роки тому

    I still miss the whteboard!

  • @ChrisBNisbet
    @ChrisBNisbet 2 роки тому

    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  2 роки тому

      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.

  • @Kahitar1
    @Kahitar1 2 роки тому

    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  2 роки тому +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!

  • @amir3515
    @amir3515 2 роки тому +1

    Come on, it's only smellz 😉

  • @stefanobailey740
    @stefanobailey740 2 роки тому

    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  2 роки тому +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 2 роки тому

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

  • @aclassypotato8155
    @aclassypotato8155 2 роки тому

    hi arjan

  • @ChazAllenUK
    @ChazAllenUK 2 роки тому

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

  • @williamwatkins6669
    @williamwatkins6669 Рік тому

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

  • @originalvbgunz
    @originalvbgunz 2 роки тому

    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  2 роки тому

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

  • @abdulahhi
    @abdulahhi 2 роки тому

    goats milk?

  • @Manas-co8wl
    @Manas-co8wl Рік тому

    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 2 роки тому

    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  2 роки тому

      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 Рік тому

    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  Рік тому

      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?