A huge thanks to you guys who are sharing more useful habits and pointing out some of the benefits that I left out in the comment section. You help both me and many other developers to understand more about this beautiful programming world! I'm super appreciative when you guys share cool information :)
It’s good practice (like he said), a lot of beginners/beginner orientated videos don’t use it though, because either they’re not writing code big enough for it to matter (most beginners use a single file), or for the beginner orientated videos they just pass over it because it’s not really important for the scope of the tutorial, however I believe its function should be taught a bit more than it is…
@@dt3688 Sorta. So, the purpose of the if statement is to explicitly check if your file is run as main (When ran directly the interpreter sets the variable __name__ to “__main__”, but when imported it’ll set __name__ to whatever your filename is.) In doing so, whatever you have inside that if __name__ == __main__ statement will be ran ONLY if you directly run that file/script. Otherwise, if you import the file, whatever you have inside the if statement will not be run. This is useful (mostly) for test code, or if your file is meant to run as both an import and a script on its own. This is to keep whatever imports your script from accidentally running the functions in the file. Ex: def PrintSomething(): print(“This will run”) if __name__ == __main__: PrintSomething() In this code, it will only print the string to your console if ran as a script directly. If you import it, PrintSomething() will not run. (Formatting may have broken for youtube with the underscores for name and main, but yk what I meant)
Another important thing with main() is the scope. Code run in "if name main" uses global scope and can/will pollute it. Running code in main has the function's scope :)
I would argue that this is _the_ most important reason to use a main() function. It's not just a way to make your code look better organised, it has fundamentally different semantics from just putting code in a top-level if-name-is-main
A main(with parameters) is even more useful when testing. Allow the if __name__ block to handle command line arguments and then pass sanitized versions of those to your main. Then your tests can just call main directly and test various scenarios.
I'm just learning python, but if you were to do this, how would you suggest you test your input parsing and sanitation itself? I'm thinking I'd have the '__name__' block call a 'parse_and_sanitise_inputs' function to do that (and pass its results to main), which could then be tested separately.
@@mnxs you would have separate files that use those functions to run some tests that test they do their job. You don't need a dedicated "parse_and_sanitise_inputs" function, for example if you want to check that you provided a correct number within a certain range, such as an age, then you would do a "parse_age" or similar function, that would check if age > 0 and is a number and return that number. I would use something like arg_parser in python
A way to avoid them becoming unreadable without resorting to loops is to split the transformations into multiple separate list comprehensions on new lines, and assigning the result of each line to a variable with a descriptive name to show what the intermediate state represents. Ideally you would use generator comprehensions as well for the intermediary steps and only convert to list at the end (if it's even needed in list form at all).
One major example I've seen, unless you've got a very descriptive comment attached or it's doing a super simple operation, don't put two for loops in one list comp
It depends how you use them. Variable Naming has a huge impact in readability as well. For example: taking the person names > 7 case, if we were to rewrite with more descriptive names it can convey a sense of reality and make the code more relatable to the readers. listLongNames = [ pp for pp in people if len(pp)>7 ] See, makes so much difference.
At 10:45 it is better to use Iterable from the typing module to annotate "elements". This way you could pass not only lists but also tuples or even generators.
@@DrDeuteron yes but the point of type hinting is to reduce the issues you have with duck typing. Duck typing is both a strength and a weakness of Python. And better type hints are more meaningful so it is encouraged to use type hints imported from the typing module.
@@kycklingmannen931 i agree, especially containers vs iterables, but what do you do for a function that takes floats, int, all the numpy versions, or arrays, matrix etc…is there super that covers all that?
for list comprehensions: a lot of common patterns can be covered by `filter` and `map`. For example, instead of doing [p for p in people if len(p) > 7] you can do filter(lambda p: len(p) > 7, people) which I find quite a lot more readable because it's explicitly telling you that the only thing happening in this statement is we're getting rid of some values based on a predicate. Although, as a counterpoint, python's lambdas and higher order functions like `filter` and `map` can be somewhat verbose so the choice is not so straightforward.
list(itertools.compress(people, map((7.).__lt__, map(len, people))) or functools.partial(operator.lt_, 7). which I like because it tells people to stay tf out of your code.
Given Comprehensions can both "filter" and "map" in one statement, that is a real advantage over filter and map. Also map and filter produce generators which can be a Gotcha when newer programmers are expecting a list. Meanwhile comprehensions give you a choice of list, set, dict and also generator, using the same syntax with just a different bracket.
@@apmcd47 the thing about providing generators is a really annoying point imo that stops me from using filter and map so much. When filtering and mapping at the same time I agree it's probably better to use a comprehension
Slight tweak to this main() thing. I think its better to have main return an int and then have sys.exit(main()) in the if name == main bit. That way the program behaves like a normal commandline program with an exit status. You can add in error handling and return a non-zero exit status easily this way.
I find that raise SystemExit(main()) is even more flexible for ease in writing end-to-end texts of the exception class exit int value that is still in python as compared to trying to write tests checking actual exit return code where you’d have to check the system after the in-test python has fully exited.
In the case of making [p for p in people if len(p) > 7] more understandable inherently, I would opt for naming, such as 'n', 'name,' 'name_str' .. [name for name in people if len(name) > 7]. Great vid!
I think the best reason to make a main function is the fact you can push utility functions to the bottom of the file and leave the *main* code at the top, not needing to scroll down to see the what the actual main code is.
4:11 thanks for this. Structuring my code to have a main function that runs the others and an if statement that runs main is a great idea to clearly show what the program does.
I was absolutely mind blown when you spoke about your type annotations. I've been watching MANY other UA-camrs and not once seen this at all! This is something that I've been practicing since! I feel this should just absolutely be normal coding 'etiquette' for the lack of a better term. Thank you, and amazing job at explaining!
Another bonus is containing the variables. You won't accidentally use a globally-scoped name in another function, if that name is local to the main instead
Actually, the best practice is to throw a not implemented error so you don't forget about it.. Honestly though, it's too much effort. I always thought python should have a keyword for this, something nice and short that throws an exception.
Just starting to learn python. That (-> None/ ->int) is amazing. I know it might not be amazing to others, but I am just learning the basics for now. I will try to watch all your videos.
On the point of "Big Functions". I think that while the core idea of breaking up large functions like this into smaller reusable functions the statement certainly is correct "Make your code as reusable as possible" often has led me down a path of overengineering. I think its more important to think about things in the domain we are programming. Do we need to know if someone is bob in multiple places? Then maybe its a good idea to write a separate function for that and use it as you demonstrated
A huge thanks to you guys who are sharing more useful habits and pointing out some of the benefits that I left out in the comment section. You help both me and many other developers to understand more about this beautiful programming world!
Comprehensions come in various flavours. There are also set { x for ...} and dict { kev: value for ...} comprehensions, as well as generator expressions ( x for ...) . The latter is interesting if performance matters and you know that you traverse the result at most once. Exceptionally good for loading tuple data into polars or pandas for example.
I've always loved list and generator comprehensions. I often each part on it's own line because I feel it becomes more readable. For example: [ p for p in people if len(p) > 7 ]
Came into the comments to add this. Comprehensions, whether list, dict, set, etc., are much more readable if on multiple lines and you can even add more conditions, though I would advise for moderation.
@@ValkoinenShiro I tend to be guilty of overdoing it; almost like I'm trying to win an award for fewest statements. There's a point at which a generator function is cleaner and more readable.
Well using main is not just prettier / more java like, it allows you to call the module externally from elsewhere which you can't do with the if statement. So there's a practical reason to do it, assuming you do intend to allow it to be called.
@@philippk5446 Often there is a point, especially with scripts. Last week for example I wrote a simple script which sends a request to update a product. I called it update product and gave it a main. It's Django so the usual entrance is run() in terminal but I also call it from admin which runs main. Then I have another update products which loops through the products and updates each one. Point is, the same script is being called from numerous places - sometimes main, sometimes not - so, yes, there is very much a point.
Fantastic breakdown of essential Python habits! The tips on if __name__ == "__main__" and type annotations are super practical. Would love more content like this to sharpen my Python skills. Keep it up! 🚀🐍
Cool video! These are great tips, but I want to point out that IMO there’s some nuance to be had with your “big function” tip, it’s important to recognize there’s costs that come with extracting code. Oftentimes you are never going to use that functionality again, extracting that code can actually make it more difficult to find certain logic just by increasing the sheer number of functions and lines of code, and it will inevitably take additional time and effort to do this extraction which can often be better used elsewhere. Reusable small functions are great, especially in large collaborative projects, but there are plenty of times where the overhead just isn’t worth it. Generally I find it isn’t worth it to abstract functionality until I actually need it somewhere else or if the function is unreadably long, and in that case you often also want to extract it into its own module
Definitely agree. I also think the example function was not "big," at only seven lines and two if statements. Breaking up big functions is often good, but I probably would have left this example function alone.
@@TheFwip But as a tutorial, it's good to plant the seed of the idea of abstraction into a beginner's mind so they're cognisant of it when it comes to coding larger functions. They may overuse it to begin with with trivial code, but with practice they will perfect that skill before the time they really need to use it.
Question: how can i do a type annotation for a function passed as a parameter of annother funcion? Ex: def do_this(arg: float): return blablabla with arg def do_that(arg: float): return blobloblo with arg def what_to_do(arg: float, func: >Type that i wanna know
A thing to mention about using the main function in your code is that anything you write directly in the if __name__ == "__main__": check will be part of the global namespace, while with main it will only be part of main's namespace, so it does have a reason beyond just a personal preference.
As I understand, it kind of depends. Nested functions aren't inherently bad- but I've seen them described as "unpythonic", and perhaps for good reason. In terms of efficiency, unless it's a monstrous function I doubt it. Python isn't famous for speed in the first place, lol. If picoseconds counted, you might just want a lower-level language. One-line nested function? Could probably be a lambda. If nothing else, makes it explicit it's a throwaway helper function. Multi-line nested function, especially if it gets used more than once? Unless there's a clear reason it shouldn't exist outside the function's scope, or you need access to variables in the 'parent' scope without passing them, you might as well move it out. It's got to grow up someday. 🙂 Too many variables to pass to another function? Indicates the need for better data organisation. Dicts are fast in Python 3.x, or you could put your data in a class.
Thanks so much for this video. You're such a life saver. Coming from an environment of using c++, I always dreaded the time I would run into an error when I run my python program simply because my editor couldn't catch the errors when I wrote them. Now with type annotations and mypy, I can begin to truly appreciate the power and simplicity of python.
12:20 "someone will say it's obvious. But in programming obvious is never obvious enough." It's not about "obvious". It is about _explicit._ It must be explicit in the code, so that other programmers can easily understand what the code does, what's its intention. But also it must be explicit so that your tools can work with it, like PyCharm here. Otherwise you need ChatGPT to look at your code and guess what's meant by "upper" in the function name. Is it a verb "convert to upper" or is it a test for upper? Good code explicitly says what it does.
Obvious is a common synonym in the English language, you're more than welcome to be pedantic and picky about words, but it's a lot of typing gone in the wind :)
@@Indently Well, explaining something like this to a non-technical person might make you sound... full of yourself, if you say obvious often. It's not obvious to everybody!
I know this is just a quick example, but I'd argue a function like is_blacklisted or similar would be better than is_bob. You wouldn't want to add another function call for every person who is banned from the club.
One additional tip for type hinting: You can still use them even if you need to maintain compatibility with older versions of Python. Instead of putting the type hints inline, you can add them to a "stub" file. It's a bit more work to build and maintain, but it gets the job done.
This is a good video and you absolutely should use type annotation. Type annotation will not help the user as stated in the video. The user most likely will be interacting with an app of some sort. Type annotation help developers who will use your code and help you, if you are using the function somewhere else in the code or reusing the function. Documentation will definitely help developers and in some instances can be used to create help for the end user.
Love this video. For the past 2 years I don't know how many times I've been helping my friends, who learn Python, to find a bug. And at the end the bug was simply using str int instead of int str. Also list comprehensions are super cool. My friends for some reason love writing list(filter(lambda ..., map(lambda ..., data ))) when you could just [int(n, 2) for n in data if len(n) == 8]. Also splitting big functions into smaller can sometimes be replaced with list comprehensions too. For example when you need to change data type with some specific data editing. While function is reusable, sometimes its functionality is very specific and unless you are good at naming things, small functions will be littering the code instead of helping
The filter/map approach is nice to write when coming from other functional programming languages. You're right that they're not always as nice to read as list comprehensions, but in certain contexts they can make a lot of sense since they allow for greater re-useability than comprehensions. Say for example you already have a function that verifies someone is an adult like so: def is_adult(person): return person.age >= 18 And that you have a function that returns the full name of a person like so: def full_name(person): return f"{person.first_name} {person.last_name}" Then at some point you want to take a list of people, and get the full names of everyone who's an adult. With list comprehension, if you wanted to re-use your existing functionality you would write the following: adult_full_names = [full_name(person) for person in people if is_adult(person)] Whereas with map/filter since they already expect functions, you dont need to invoke them, just pass them as is: adult_full_names = map(full_name, filter(is_adult, people)) Obviously I left off the list() conversion in that, but in most cases with functional-style code you wouldn't be converting to lists very often, since leaving the data in generator form is much more efficient as each element can be streamed through multiple transformations without creating a bunch of intermediate lists. It allows you to write your code as if python was a lazy language like haskell, where values are only calculated when they're actually used/required. So basically I would use map/filter if I already have functions for the transformation I'll be doing, but if I would be having to write a lambda inside the map/filter, then I would instead use a list comprehension (or more likely a generator comprehension, again for performance reasons).
I was thinking about filter while I was writing the list comprehension, but the instant benefit of using the list comprehension for me was that I could write it out in a split second, while with filter I always need to hover over the function to see the little pop-up with the documentation to make sure I'm passing in things in the correct order.
@@LittleLily_ That is a very good point. In my example I pointed on using map() and filter() with lambda-functions. Usually those lambda-functions make code hard to read. I thing using existing functions is very clean Also I was talking about people, who is new at Python. So usually I can see my friends making map -> filter -> ... -> filter combinations. In such cases even using existing functions does not save readability. My point was that beginners sometimes make overcomplicated map/filter combinations that can be replaced with a single much more readable list comprehension
@@Indently that's fair enough. the order of the arguments for higher order functions in any language generally always has the function first and data second, as that way you can more easily partially apply the function, for example with python you might write this: from functools import partial def full_name(person): return f"{person.first_name} {person.last_name}" people_to_full_names = partial(map, full_name) And now you have a reusable function that can operate on any list of people to return the full name of all of them like so: full_names_a = people_to_full_names(people_a) full_names_b = people_to_full_names(people_b)
Thank you for posting. Just one comment: @4:57, you begin describing "big functions" with type annotations(also called type hints), but you describe type annotations afterwards in the next section. Shouldn't you change the order around to describe type annotations before you describe big functions?
imo, large functions are not bad. make functions as big as necessary without repeating yourself... if you notice patterns/checks that are used more than once throughout the code, only then should you extract them in separate function...
@@yuhongwang9052 how so? what fundamentally makes shorter functions easier to test? you still have to hit same number of branch paths/combinations if you were to split larger function into smaller ones.
Yes, but it's a lot more obvious when a function is smaller. And more importantly, when a smaller function fails, you know exactly what's wrong. This may not be the case if there are 2^13 (13 if statements) instead of 2^3
@@saltybaguette7683 how is it more obvious when function is smaller? you should name your test cases meaningful names and target only that case in that particular unit. you can still know exactly where your function is wrong because you see which test names failed and which passed for that function. it has nothing to do with function size.
For the first two tips i would encourage to write real tests instead of "tests inside a module". Yes, it will take extra time, but will serve much better.
Some of these are definitely bad habits. Definitely much better to do it this way, by writing a unit test. Funny to mention "testing" but not use any built-in tests idiomatic to the language... It won't actually take more time since the execution environment will be more predictable and you avoid all the issues while having better design of code.
For simple scripts intended for command line use I do this: import sys def main(arg1, arg2): ... if __name__ == '__main__': main(*sys.argv[1:]) # The first argument is the script's name, skip it.
I really like the "big functions" part but I have a few questions: 1. how do I tell the difference between more "top-layer" functions and these small mini functions that just make things more abstract? i mean, in terms of naming, often you would want to be bale to have a good hierarchy of this. 2. I'm not sure if this example is the best because one also needs to understand at which point should this separation happen. In practise the example probably should stay as it is, so that the code doesn't become more complex and hard to read.
I've been following you for few months now, you provide amazing content, and I really appreciate your videos. Thank you so much. You are helping so many students😊
Thanks so much for the great video on good Python habits. I know you put a lot of time and effort into it, and it shows. The video is well done and easy to understand.
16:50 - true. You could use Ruby instead to have nicer code: long_names = people.select{|p| p.length > 7} and if you need them in upper case too: long_names = people.select{|p| p.length > 7}.collect{|p| p.upcase}
similar to how you'd write it in JS/TS: longNames = people.filter(p => p.length > 7) longNames = people.filter(p => p.length > 7).map(p => p.toUpperCase()) the lambda syntax in python isn't quite as nice, so I think that's why most people don't like using stuff like map/filter over list comprehensions in python.
The problem with list comprehensions is not the syntax but how you layout the comprehension. I prefer to put everything on a new line as it makes it more readable: long_names = [ p.upper() # list element for p in people # iteration if len(p) > 7 # condition ] and it is more readable then using filter and map.
As someone who learned C before python, I immediately looked up if I could create an "int main()" equivalent, thus I always used the if __name__ == '__main__' out of habit and preference
i have to agree with most but id say sometimes doing one big function is the way to go. sometimes stuff is so intertwined and so very unlikely to get used anywhere else, that its just better to have it all in one place rather than split between a bunch of smaller functions. essentially write it the same though, by splitting it into smaller parts and denoting when some "step" has been achieved. if you ever do find a piece needs to be reused it isnt usually too hard to just pull that piece out into its own function later on
You don't need to annotate variables that are declared unless you have good reason (either it is mutable by an end user, or the type cannot otherwise be inferred). It's a waste of time to annotate everything like that. Even mypy with --strict doesn't care.
I first learnt Python in version 2. I didn’t know half of these things were available now because they weren’t in Python 2. I did try to learn the differences when v 3 came out, but I think a lot of these were added after the first version of 3? Anyway thanks for broadening my horizons.
I clicked on this out of curiosity (I don't work with Python), but holy moly, what font is that?! I don't know if it's the size, rendering or what, but it looks sooooo clean. I need that.
Nice video! Can you make a video about pypi? It has become difficult and confusing to publish a python package, it would be appreciated if you make a tuto about it! Thank you!!
your editor shows the number of usages at the beginning of the function. I have googled(clearly the wrong words) to figure out how to turn that on in Pycharm. Hints?
But why would you directly call that function in your module file? Shouldn't functions be called in your main and not module file? You may need to call a function in another function but you shouldn't be calling anything outside of the modules. I use the module files, for just defining functions and call them in main...
Adding doctrings is not a lost of time.. Docstrings appears when someone does function?, and can be used to generate documentation automatically with tools like Sphinx.
I've yet to see a useful example of writing a variable type annotation. It is useful to write input and output type annotations for functions, but for variable it can be inferred and you don't want it to fail if you change the value - subsequent function calls will fail if needed, so everything is good
Tip: Instead of creating a specific is_bob function, try making an is_banned function, which receives a name and a blacklist, and returns if such name is in the blacklist
Putting anything in ___main___ instead of main() is dangerous because variables in ___main___ will have a script-wide scope instead of function-wide, which can lead to surprizes. It's best to use ___main___ just to call main() unless global variables are intended.
Please make video how actually behind the scenes in python, series of memory allocation initiated after execution of any function, recursion function, closures, and decorators. I'm confusing whenever I visualizes all of this things in my mind.. Please make our foundation clear.
Thank you for your great videos about Python programming. Since I love functional programming, would you please tell us about Python's functional features, map and filter, for example?
@@DrDeuteron Amazon sells some books about functional programming in Python, But Python isn't purely functional. It's object oriented. In a purely functional language, there are expressions instead of statements. To simulate looping, you need recursion. Since each thing is an expression in a purely functional language, each "if" expression needs an else clause, even when you wouldn't need one in a procedural language. That's because each expression must reduce to a value. So suppose I write this: if even n then n else n + 1 Then the number will replace the "if," "then," and "else" expressions. And the computer will remember the number until your program terminates.
I absolutely would. I started out with Java and learned a lot of important concepts that helped with Python, if you're more dedicated you can even go lower and start with C. Java was enough for my profession.
Maybe I'm a beginner but what is the reason to have "main " between""__"" ? Is there any use. Further more why did you not have error when checking if __name__==__main__ As __name__ as no value affected
__name__ is automatically set to "__main__" when the script is run normally. If it is imported, __name__ is a different value which I'm not entirely sure about.
By default, the name of the Python main module is always "__main__", i.e. when it is being run as a script. If it were to be imported, the __name__ when become that of the module.
variables with trailing underscores are understood as a special type, and are already declared and initialized when the script runs. '_name_' will adopt the string name of the current module which defaults to '_main_' if the script is being run directly. Otherwise the string will take the name of the import during execution. so when the script executes and name is not main (in other words the code was imported), the code block to run on compiling will be skipped, and the program will act as an object.
Type annotations saved Python for me. I apparently really rely on type annotations in more complicated code. I was doing type definitions in comments until realized it was an option in the language.
@@Indently thanks for your fast reply! Btw I have a discord server made for friends who also learn python and your channel have great tips on how to learn python faster. So thank you for your teaching in a way! It is accessible and easy to understand :)
Everything was solid up to the p for p in people I don't use any single character variables anymore it doesn't help with readability and I'm not trying to minify everything if the goal is to make it readable. I would just use a list comprehension when absolutely necessary.
Regarding Big Functions. It sounds really similar to the argument that you should write all your small functions directly in cython. That way even if performance is not a concern right now those functions can be reused in potential other future parts of the code where performance might matter making your code more reusable. To that I would say: stop it, get some help. Just write the code and after it works if you see you have a performance bottleneck or a good reason to split off functions (code duplication or wanting to test only a part of a function) than do the work instead of always doing it prematurely. Stop optimizing for potential future scenarios at the cost of the present.
My only recommendation is that you plan your code before you write your code. Living in the moment is only advised if your program is as big as printing "hello, world".
For number 1. if __name__ == __main__ is ment to be used in modules that should be run not for testing porpouses. You should test your code with unittest in separate module
Yes, in an ideal world where you also always have all the time you need to make perfect fully documented code. Here in the real world we need to prioritize stuff. So for code that is most likely only being used by myself, and maybe a few collegues, for a few tasks. I always put testing in the 'if main' section. The test cases in there also function as a minimum documentation on the intended use of that module. I do this because here in the real world, insisting on full fledged unit test and other "good practices", only means that test and doc is skipped entirely for such low use code. Sometimes such code do end up being more useful, and more used. And needs to be upgraded to production ready code (unit test etc) Ask your self "would I prefer to upgrade a module with test cases in the 'if main', or a module where test was done on command line on some unknown pc?"
@@martin_hansen I disagree wih you. if __name__ == "__main__" statement indicates that this module is ment to be used. And about "real world". I have only 3+ years of proffessional experience with python but in each project (but he last one) we had tests. lots of test. In fact there was much more code for test than for real functionality. It may look like unnecessary work but it is very helpfull and much needed in large projects. On the other hand, I'm working with quite small project without any tests and it is nightmare. PS. I understand that with your own project you do not unittest (I dont myself) because delivering functionality is more fun :)
I have no argument against people who extensively test their code, you guys are the true heroes who really go out of your way to make your apps reliable, and I respect it. For some of us though, when we are building modules, we tend to test functionality as we write it. The complete opposite of TDD. Forgetting those function calls in a module you created can be a headache if you don't use the "if name is equal to main" check.
If _name__ == "__main__" foo() Does NOT indicate module is ment to be run. It is the exact opposite. The whole reason of adding this condition is to shield the call to foo() from being called when it is imported by another module. If the module is intended to just be run and not imported, you should just put foo() So the 'if _name__ == "__main__"' statement tells me that this module was intended to be imported but can also be run.
I understand what you're thinking Martin, but if you add an "if name is equal to main check" anywhere in your code, it will tell anyone who sees the script that something was meant to be run directly. Whether it's just for personal testing purposes, or to prevent it from being executed when being imported. It tells whoever is reading it, that there is functionality that can be executed directly in that script. I would never run "foo()" out in the open like that. You will have to track that call down later if you forget about it when you are importing the module that contains it.
I think the most important python habit I have is to write as little code as possible in python, because python is a programming language by mathematicians and nowadays data scientist for themselves, and not actual software developers.
@6:22 Since AND is a shortcut operator, you need to rewrite that: return has_id AND age >= 21. If they don't have ID, they can't prove their age. So if has_id is false, the comparison age >= 21 is never checked. The way you wrote it, the comparison would be checked with no proof of age!! 🙂 But seriously, since your premise here is good Python habits, I thought this needed to be mentioned.
pretty good vid. Not a fan of the "is_bob" function as it has hard coded value in the function name without describing what it does. I would prefer something like is_blacklisted with a bliacklist: list[str] = ['bob'] inside or someplace where it can be obtained. Now you don't need to refactor if the name is really "Robert"
is_blacklisted is a much better real world example. It was supposed to be an amusing example where in this world Bob was the only one that would ever be blacklisted. But yes, in real life, you'd probably have many more people blacklisted than just Bob :)
@@Indently agreed.. I know what you were doing and it was a good point. I was just throwing in some additional contribution. You'd be surprised (or maybe you wouldn't) at just how bad function naming and structure can be and how it can impact changes to program requirements
A huge thanks to you guys who are sharing more useful habits and pointing out some of the benefits that I left out in the comment section. You help both me and many other developers to understand more about this beautiful programming world!
I'm super appreciative when you guys share cool information :)
after hour and hours and trainings, someone explains in 3 seconds the role of if __name__ = ''__main__' ,omg, thx a lot !
100000%
if I get a script w/o it, I freak out.
It’s good practice (like he said), a lot of beginners/beginner orientated videos don’t use it though, because either they’re not writing code big enough for it to matter (most beginners use a single file), or for the beginner orientated videos they just pass over it because it’s not really important for the scope of the tutorial, however I believe its function should be taught a bit more than it is…
Still don’t really understand so it’s like if the module is directly imported from the main file then run?
@@dt3688 Sorta. So, the purpose of the if statement is to explicitly check if your file is run as main (When ran directly the interpreter sets the variable __name__ to “__main__”, but when imported it’ll set __name__ to whatever your filename is.)
In doing so, whatever you have inside that if __name__ == __main__ statement will be ran ONLY if you directly run that file/script. Otherwise, if you import the file, whatever you have inside the if statement will not be run.
This is useful (mostly) for test code, or if your file is meant to run as both an import and a script on its own. This is to keep whatever imports your script from accidentally running the functions in the file. Ex:
def PrintSomething():
print(“This will run”)
if __name__ == __main__:
PrintSomething()
In this code, it will only print the string to your console if ran as a script directly. If you import it, PrintSomething() will not run.
(Formatting may have broken for youtube with the underscores for name and main, but yk what I meant)
Another important thing with main() is the scope. Code run in "if name main" uses global scope and can/will pollute it. Running code in main has the function's scope :)
I would argue that this is _the_ most important reason to use a main() function. It's not just a way to make your code look better organised, it has fundamentally different semantics from just putting code in a top-level if-name-is-main
True, but you should never ever run a main function AND THEN run extra code in the global scope later, so namespace pollution doesn’t rlly matter.
I didn't think about that, the arguments in the video didn't convince me at all, but this is a really good reason to use a main funciton.
What do you mean by "pollution"?
@@AmodeusR I think he means having a bunch of variables exposed to the global scope is polluting it, and can be problematic when importing a module.
A main(with parameters) is even more useful when testing. Allow the if __name__ block to handle command line arguments and then pass sanitized versions of those to your main. Then your tests can just call main directly and test various scenarios.
I'm just learning python, but if you were to do this, how would you suggest you test your input parsing and sanitation itself? I'm thinking I'd have the '__name__' block call a 'parse_and_sanitise_inputs' function to do that (and pass its results to main), which could then be tested separately.
@@mnxs you would have separate files that use those functions to run some tests that test they do their job. You don't need a dedicated "parse_and_sanitise_inputs" function, for example if you want to check that you provided a correct number within a certain range, such as an age, then you would do a "parse_age" or similar function, that would check if age > 0 and is a number and return that number.
I would use something like arg_parser in python
Just one thing about list comprehensions: they can very easily become very hard to read, so I'd say "don't overuse them"
A way to avoid them becoming unreadable without resorting to loops is to split the transformations into multiple separate list comprehensions on new lines, and assigning the result of each line to a variable with a descriptive name to show what the intermediate state represents. Ideally you would use generator comprehensions as well for the intermediary steps and only convert to list at the end (if it's even needed in list form at all).
One major example I've seen, unless you've got a very descriptive comment attached or it's doing a super simple operation, don't put two for loops in one list comp
Exactly!
It depends how you use them. Variable Naming has a huge impact in readability as well.
For example: taking the person names > 7 case, if we were to rewrite with more descriptive names it can convey a sense of reality and make the code more relatable to the readers.
listLongNames = [ pp for pp in people if len(pp)>7 ]
See, makes so much difference.
["["".join(letter) for letter in word if letter.isalpha()] for word in "why would you say that?".split()
]
At 10:45 it is better to use Iterable from the typing module to annotate "elements". This way you could pass not only lists but also tuples or even generators.
it's called duck typing.
@@DrDeuteron yes but the point of type hinting is to reduce the issues you have with duck typing. Duck typing is both a strength and a weakness of Python. And better type hints are more meaningful so it is encouraged to use type hints imported from the typing module.
@@kycklingmannen931 i agree, especially containers vs iterables, but what do you do for a function that takes floats, int, all the numpy versions, or arrays, matrix etc…is there super that covers all that?
Hats off to my main man Bob, he did nothing wrong.
:3
for list comprehensions: a lot of common patterns can be covered by `filter` and `map`. For example, instead of doing [p for p in people if len(p) > 7] you can do filter(lambda p: len(p) > 7, people) which I find quite a lot more readable because it's explicitly telling you that the only thing happening in this statement is we're getting rid of some values based on a predicate.
Although, as a counterpoint, python's lambdas and higher order functions like `filter` and `map` can be somewhat verbose so the choice is not so straightforward.
list(itertools.compress(people, map((7.).__lt__, map(len, people)))
or
functools.partial(operator.lt_, 7).
which I like because it tells people to stay tf out of your code.
@@DrDeuteron ahahahahhaha
@@DrDeuteronlol
Given Comprehensions can both "filter" and "map" in one statement, that is a real advantage over filter and map. Also map and filter produce generators which can be a Gotcha when newer programmers are expecting a list. Meanwhile comprehensions give you a choice of list, set, dict and also generator, using the same syntax with just a different bracket.
@@apmcd47 the thing about providing generators is a really annoying point imo that stops me from using filter and map so much. When filtering and mapping at the same time I agree it's probably better to use a comprehension
Slight tweak to this main() thing. I think its better to have main return an int and then have sys.exit(main()) in the if name == main bit. That way the program behaves like a normal commandline program with an exit status. You can add in error handling and return a non-zero exit status easily this way.
That's a useful tip if you're building a more complex script, but probably a bit outside what I'd put in a boilerplate.
I find that raise SystemExit(main()) is even more flexible for ease in writing end-to-end texts of the exception class exit int value that is still in python as compared to trying to write tests checking actual exit return code where you’d have to check the system after the in-test python has fully exited.
In the case of making [p for p in people if len(p) > 7] more understandable inherently, I would opt for naming, such as 'n', 'name,' 'name_str' .. [name for name in people if len(name) > 7]. Great vid!
I think the best reason to make a main function is the fact you can push utility functions to the bottom of the file and leave the *main* code at the top, not needing to scroll down to see the what the actual main code is.
4:11 thanks for this. Structuring my code to have a main function that runs the others and an if statement that runs main is a great idea to clearly show what the program does.
I was absolutely mind blown when you spoke about your type annotations. I've been watching MANY other UA-camrs and not once seen this at all! This is something that I've been practicing since! I feel this should just absolutely be normal coding 'etiquette' for the lack of a better term. Thank you, and amazing job at explaining!
For number 2, another bonus is if you declare a main function you can expose it in an init or import it. One example is importing when testing.
Another bonus is containing the variables. You won't accidentally use a globally-scoped name in another function, if that name is local to the main instead
Thanks for sharing the benefits guys! I often forget to mention simple things like these when creating the videos as a one man team.
It's similarly useful if you're using asyncio.
don't use "..." for a placeholder, use "pass".
Whats the advantage of pass vs …?
@@NathanSMS26 I think pass is more visually obvious than the ellipse, but if you prefer the ellipse, then enjoy :)
Actually, the best practice is to throw a not implemented error so you don't forget about it.. Honestly though, it's too much effort. I always thought python should have a keyword for this, something nice and short that throws an exception.
@@davidmurphy563 "assert false"
@@Zaniahiononzenbei Oh, that's a good one! I'm going to use that!
Just starting to learn python. That (-> None/ ->int) is amazing. I know it might not be amazing to others, but I am just learning the basics for now. I will try to watch all your videos.
This came at PRECISELY the right time for the type annotations stuff 🎉 big thanks
9:50 before you use a function, you should know, what it does. And you can insert every type which provides an upper-function and not only strings.
On the point of "Big Functions". I think that while the core idea of breaking up large functions like this into smaller reusable functions the statement certainly is correct "Make your code as reusable as possible" often has led me down a path of overengineering. I think its more important to think about things in the domain we are programming. Do we need to know if someone is bob in multiple places? Then maybe its a good idea to write a separate function for that and use it as you demonstrated
A huge thanks to you guys who are sharing more useful habits and pointing out some of the benefits that I left out in the comment section. You help both me and many other developers to understand more about this beautiful programming world!
Comprehensions come in various flavours. There are also set { x for ...} and dict { kev: value for ...} comprehensions, as well as generator expressions ( x for ...) .
The latter is interesting if performance matters and you know that you traverse the result at most once. Exceptionally good for loading tuple data into polars or pandas for example.
I've always loved list and generator comprehensions. I often each part on it's own line because I feel it becomes more readable. For example:
[
p
for p in people
if len(p) > 7
]
Came into the comments to add this. Comprehensions, whether list, dict, set, etc., are much more readable if on multiple lines and you can even add more conditions, though I would advise for moderation.
@@ValkoinenShiro I tend to be guilty of overdoing it; almost like I'm trying to win an award for fewest statements. There's a point at which a generator function is cleaner and more readable.
@@TheJaguar1983 "clever" is not a compliment in python
Well using main is not just prettier / more java like, it allows you to call the module externally from elsewhere which you can't do with the if statement. So there's a practical reason to do it, assuming you do intend to allow it to be called.
yeah but there is no point in calling a main from somewhere else! If you need to do that, the function you wrote is not a main.
@@philippk5446 Often there is a point, especially with scripts. Last week for example I wrote a simple script which sends a request to update a product. I called it update product and gave it a main. It's Django so the usual entrance is run() in terminal but I also call it from admin which runs main. Then I have another update products which loops through the products and updates each one.
Point is, the same script is being called from numerous places - sometimes main, sometimes not - so, yes, there is very much a point.
Fantastic breakdown of essential Python habits! The tips on if __name__ == "__main__" and type annotations are super practical. Would love more content like this to sharpen my Python skills. Keep it up! 🚀🐍
Cool video! These are great tips, but I want to point out that IMO there’s some nuance to be had with your “big function” tip, it’s important to recognize there’s costs that come with extracting code. Oftentimes you are never going to use that functionality again, extracting that code can actually make it more difficult to find certain logic just by increasing the sheer number of functions and lines of code, and it will inevitably take additional time and effort to do this extraction which can often be better used elsewhere.
Reusable small functions are great, especially in large collaborative projects, but there are plenty of times where the overhead just isn’t worth it.
Generally I find it isn’t worth it to abstract functionality until I actually need it somewhere else or if the function is unreadably long, and in that case you often also want to extract it into its own module
Definitely agree. I also think the example function was not "big," at only seven lines and two if statements.
Breaking up big functions is often good, but I probably would have left this example function alone.
As someone who has had to comb through dozens of 3-5 line functions to understand what a script is doing, I agree.
@@TheFwip But as a tutorial, it's good to plant the seed of the idea of abstraction into a beginner's mind so they're cognisant of it when it comes to coding larger functions. They may overuse it to begin with with trivial code, but with practice they will perfect that skill before the time they really need to use it.
Question: how can i do a type annotation for a function passed as a parameter of annother funcion? Ex:
def do_this(arg: float): return blablabla with arg
def do_that(arg: float): return blobloblo with arg
def what_to_do(arg: float, func: >Type that i wanna know
You'll need to import a special type from the typing module.
from typing import Callable
def what_to_do(arg: float, func: Callable):
A thing to mention about using the main function in your code is that anything you write directly in the if __name__ == "__main__": check will be part of the global namespace, while with main it will only be part of main's namespace, so it does have a reason beyond just a personal preference.
In 3rd example, whether calling nested functions will make program more inefficient?
As I understand, it kind of depends. Nested functions aren't inherently bad- but I've seen them described as "unpythonic", and perhaps for good reason.
In terms of efficiency, unless it's a monstrous function I doubt it. Python isn't famous for speed in the first place, lol. If picoseconds counted, you might just want a lower-level language.
One-line nested function? Could probably be a lambda. If nothing else, makes it explicit it's a throwaway helper function.
Multi-line nested function, especially if it gets used more than once? Unless there's a clear reason it shouldn't exist outside the function's scope, or you need access to variables in the 'parent' scope without passing them, you might as well move it out. It's got to grow up someday. 🙂
Too many variables to pass to another function? Indicates the need for better data organisation. Dicts are fast in Python 3.x, or you could put your data in a class.
The python interpreter is so inefficient, you won't notice the difference anyway.
I strive to separate printing/logging from logic. in your example i would make the enter_club method return bool and handle the prints in main()
Thanks so much for this video. You're such a life saver. Coming from an environment of using c++, I always dreaded the time I would run into an error when I run my python program simply because my editor couldn't catch the errors when I wrote them. Now with type annotations and mypy, I can begin to truly appreciate the power and simplicity of python.
I'm incredibly happy to hear you enjoy the type annotations :)
12:20 "someone will say it's obvious. But in programming obvious is never obvious enough." It's not about "obvious". It is about _explicit._ It must be explicit in the code, so that other programmers can easily understand what the code does, what's its intention. But also it must be explicit so that your tools can work with it, like PyCharm here. Otherwise you need ChatGPT to look at your code and guess what's meant by "upper" in the function name. Is it a verb "convert to upper" or is it a test for upper? Good code explicitly says what it does.
Obvious is a common synonym in the English language, you're more than welcome to be pedantic and picky about words, but it's a lot of typing gone in the wind :)
@@Indently Well, explaining something like this to a non-technical person might make you sound... full of yourself, if you say obvious often. It's not obvious to everybody!
Super appreciate this. Need to include type hinting into my coding. Your explanations make sense.
I really like how you explain everything, it’s very clear. Thanks a lot
I know this is just a quick example, but I'd argue a function like is_blacklisted or similar would be better than is_bob. You wouldn't want to add another function call for every person who is banned from the club.
4:25 stack memory: am i just a joke to you 😂
Actually incredible top tier tips, 10/10, would watch again.
One additional tip for type hinting: You can still use them even if you need to maintain compatibility with older versions of Python. Instead of putting the type hints inline, you can add them to a "stub" file. It's a bit more work to build and maintain, but it gets the job done.
This is a good video and you absolutely should use type annotation. Type annotation will not help the user as stated in the video. The user most likely will be interacting with an app of some sort. Type annotation help developers who will use your code and help you, if you are using the function somewhere else in the code or reusing the function. Documentation will definitely help developers and in some instances can be used to create help for the end user.
Another great video with great examples. Thank you. Mypy, who knew?
You can also use a Strong Type language like Swift which does all this work by the compiler instead of the Text editor and it is much more safer
Love this video. For the past 2 years I don't know how many times I've been helping my friends, who learn Python, to find a bug. And at the end the bug was simply using str int instead of int str. Also list comprehensions are super cool. My friends for some reason love writing list(filter(lambda ..., map(lambda ..., data ))) when you could just [int(n, 2) for n in data if len(n) == 8]. Also splitting big functions into smaller can sometimes be replaced with list comprehensions too. For example when you need to change data type with some specific data editing. While function is reusable, sometimes its functionality is very specific and unless you are good at naming things, small functions will be littering the code instead of helping
The filter/map approach is nice to write when coming from other functional programming languages. You're right that they're not always as nice to read as list comprehensions, but in certain contexts they can make a lot of sense since they allow for greater re-useability than comprehensions.
Say for example you already have a function that verifies someone is an adult like so:
def is_adult(person):
return person.age >= 18
And that you have a function that returns the full name of a person like so:
def full_name(person):
return f"{person.first_name} {person.last_name}"
Then at some point you want to take a list of people, and get the full names of everyone who's an adult. With list comprehension, if you wanted to re-use your existing functionality you would write the following:
adult_full_names = [full_name(person) for person in people if is_adult(person)]
Whereas with map/filter since they already expect functions, you dont need to invoke them, just pass them as is:
adult_full_names = map(full_name, filter(is_adult, people))
Obviously I left off the list() conversion in that, but in most cases with functional-style code you wouldn't be converting to lists very often, since leaving the data in generator form is much more efficient as each element can be streamed through multiple transformations without creating a bunch of intermediate lists. It allows you to write your code as if python was a lazy language like haskell, where values are only calculated when they're actually used/required.
So basically I would use map/filter if I already have functions for the transformation I'll be doing, but if I would be having to write a lambda inside the map/filter, then I would instead use a list comprehension (or more likely a generator comprehension, again for performance reasons).
I was thinking about filter while I was writing the list comprehension, but the instant benefit of using the list comprehension for me was that I could write it out in a split second, while with filter I always need to hover over the function to see the little pop-up with the documentation to make sure I'm passing in things in the correct order.
@@LittleLily_ That is a very good point. In my example I pointed on using map() and filter() with lambda-functions. Usually those lambda-functions make code hard to read. I thing using existing functions is very clean
Also I was talking about people, who is new at Python. So usually I can see my friends making map -> filter -> ... -> filter combinations. In such cases even using existing functions does not save readability. My point was that beginners sometimes make overcomplicated map/filter combinations that can be replaced with a single much more readable list comprehension
@@Indently that's fair enough. the order of the arguments for higher order functions in any language generally always has the function first and data second, as that way you can more easily partially apply the function, for example with python you might write this:
from functools import partial
def full_name(person):
return f"{person.first_name} {person.last_name}"
people_to_full_names = partial(map, full_name)
And now you have a reusable function that can operate on any list of people to return the full name of all of them like so:
full_names_a = people_to_full_names(people_a)
full_names_b = people_to_full_names(people_b)
@@cosy_sweater "pointed" --> "punted" maybe?
Thank you for posting. Just one comment:
@4:57, you begin describing "big functions" with type annotations(also called type hints), but you describe type annotations afterwards in the next section. Shouldn't you change the order around to describe type annotations before you describe big functions?
Could have been a more clever approach, yes :)
imo, large functions are not bad. make functions as big as necessary without repeating yourself... if you notice patterns/checks that are used more than once throughout the code, only then should you extract them in separate function...
IMO, large functions are hard to unit test.
@@yuhongwang9052 how so? what fundamentally makes shorter functions easier to test? you still have to hit same number of branch paths/combinations if you were to split larger function into smaller ones.
Yes, but it's a lot more obvious when a function is smaller.
And more importantly, when a smaller function fails, you know exactly what's wrong. This may not be the case if there are 2^13 (13 if statements) instead of 2^3
@@saltybaguette7683 how is it more obvious when function is smaller? you should name your test cases meaningful names and target only that case in that particular unit. you can still know exactly where your function is wrong because you see which test names failed and which passed for that function. it has nothing to do with function size.
For the first two tips i would encourage to write real tests instead of "tests inside a module". Yes, it will take extra time, but will serve much better.
how about a video on unittest or the current most bestest argparse?
Some of these are definitely bad habits.
Definitely much better to do it this way, by writing a unit test. Funny to mention "testing" but not use any built-in tests idiomatic to the language... It won't actually take more time since the execution environment will be more predictable and you avoid all the issues while having better design of code.
You should use `List` from `typing`, insted of `list` to make linters work well.
For simple scripts intended for command line use I do this:
import sys
def main(arg1, arg2):
...
if __name__ == '__main__':
main(*sys.argv[1:]) # The first argument is the script's name, skip it.
3:08 I love programming in Java man it only made me wanna watch more!
having a psvm alike functionality in python is nice, cool tip :)
I really like the "big functions" part but I have a few questions:
1. how do I tell the difference between more "top-layer" functions and these small mini functions that just make things more abstract?
i mean, in terms of naming, often you would want to be bale to have a good hierarchy of this.
2. I'm not sure if this example is the best because one also needs to understand at which point should this separation happen. In practise the example probably should stay as it is, so that the code doesn't become more complex and hard to read.
I've been following you for few months now, you provide amazing content, and I really appreciate your videos. Thank you so much. You are helping so many students😊
Thanks so much for the great video on good Python habits. I know you put a lot of time and effort into it, and it shows. The video is well done and easy to understand.
As a beginner I found these habits very helpful thanks for providing
The point of 2 isn't a "feeling" or anything like that. It's: a) testable, and b) importable from another package.
16:50 - true. You could use Ruby instead to have nicer code:
long_names = people.select{|p| p.length > 7}
and if you need them in upper case too:
long_names = people.select{|p| p.length > 7}.collect{|p| p.upcase}
you could also use filter an map in py, iirc
similar to how you'd write it in JS/TS:
longNames = people.filter(p => p.length > 7)
longNames = people.filter(p => p.length > 7).map(p => p.toUpperCase())
the lambda syntax in python isn't quite as nice, so I think that's why most people don't like using stuff like map/filter over list comprehensions in python.
The problem with list comprehensions is not the syntax but how you layout the comprehension. I prefer to put everything on a new line as it makes it more readable:
long_names = [
p.upper() # list element
for p in people # iteration
if len(p) > 7 # condition
]
and it is more readable then using filter and map.
@@LittleLily_ why does "p =>" appear in every call signature? what else is there? seems like redundancy...Guido don't like.
@@DrDeuteron Its no different to the |p| in the ruby example, it's just how you define an anonymous function with an input argument named p
As someone who learned C before python, I immediately looked up if I could create an "int main()" equivalent, thus I always used the if __name__ == '__main__' out of habit and preference
i have to agree with most but id say sometimes doing one big function is the way to go. sometimes stuff is so intertwined and so very unlikely to get used anywhere else, that its just better to have it all in one place rather than split between a bunch of smaller functions. essentially write it the same though, by splitting it into smaller parts and denoting when some "step" has been achieved. if you ever do find a piece needs to be reused it isnt usually too hard to just pull that piece out into its own function later on
You don't need to annotate variables that are declared unless you have good reason (either it is mutable by an end user, or the type cannot otherwise be inferred). It's a waste of time to annotate everything like that. Even mypy with --strict doesn't care.
Today I encountered the first problem while I was learning Python.
Surprised to see this video popped up to my scroll and I’m glad I watched this.
I first learnt Python in version 2. I didn’t know half of these things were available now because they weren’t in Python 2. I did try to learn the differences when v 3 came out, but I think a lot of these were added after the first version of 3? Anyway thanks for broadening my horizons.
for some reason my type aannotations still don't work "sample: list[int] = ['a', 1]" in such cases (yes, I installed mypy successfully)
Very nicely put together, thanks!
I clicked on this out of curiosity (I don't work with Python), but holy moly, what font is that?! I don't know if it's the size, rendering or what, but it looks sooooo clean. I need that.
The best __name__ = 'main' explanation i've ever seen and im writing this comment just 2 minutes in the video.
you're really helpful man, I'll recommend your videos to my friends
In number 3, what is None: 4 usages, and None: 1 usage beneath ?
When i type this, i get "invalid syntax"
ok but what does putting a var in front of for actually do? It looked cool but still no idea how to use it
I’m glad! Your videos become my scripts raise to another level
Besides comprehensions I prefer to use map or filter. I am not sure, but I think it's faster
Nice video! Can you make a video about pypi? It has become difficult and confusing to publish a python package, it would be appreciated if you make a tuto about it! Thank you!!
Very good tips, thank you. It would be useful to copy/paste some of the code demonstrated, as I'm slow/bad at typing. :-)
How do you configure pycharm to check types with mypy ?
There's an extension in the settings that you can install
Thanks for the video. Very clear explanations!
Glad you enjoyed it!
your editor shows the number of usages at the beginning of the function. I have googled(clearly the wrong words) to figure out how to turn that on in Pycharm. Hints?
Mine PyCharm does this by default
which color theme are you using?
that is the default theme of pycharm
But why would you directly call that function in your module file? Shouldn't functions be called in your main and not module file? You may need to call a function in another function but you shouldn't be calling anything outside of the modules. I use the module files, for just defining functions and call them in main...
Adding doctrings is not a lost of time.. Docstrings appears when someone does function?, and can be used to generate documentation automatically with tools like Sphinx.
what if I have a function that can return more than one type of data, based in some conditions?
is it an extension what says '1 usage', '2 usage' in the functions?
I've yet to see a useful example of writing a variable type annotation. It is useful to write input and output type annotations for functions, but for variable it can be inferred and you don't want it to fail if you change the value - subsequent function calls will fail if needed, so everything is good
What is the IDE used in this video? Pycharm or VS code ? 🙏
PyCharm
Tip: Instead of creating a specific is_bob function, try making an is_banned function, which receives a name and a blacklist, and returns if such name is in the blacklist
Putting anything in ___main___ instead of main() is dangerous because variables in ___main___ will have a script-wide scope instead of function-wide, which can lead to surprizes. It's best to use ___main___ just to call main() unless global variables are intended.
Please make video how actually behind the scenes in python, series of memory allocation initiated after execution of any function, recursion function, closures, and decorators. I'm confusing whenever I visualizes all of this things in my mind.. Please make our foundation clear.
I'd leave if you said C#(or some DephiCase language). but Java or Haskell would intrigue me.
For the self-documentating part around 11:30 ish, does this only work with PyCharm?
Thank you for your great videos about Python programming. Since I love functional programming, would you please tell us about Python's functional features, map and filter, for example?
try some itertools, though I don't know if that is formally functional.
Don't forget lambda.
@@DrDeuteron Amazon sells some books about functional programming in Python, But Python isn't purely functional. It's object oriented. In a purely functional language, there are expressions instead of statements. To simulate looping, you need recursion.
Since each thing is an expression in a purely functional language, each "if" expression needs an else clause, even when you wouldn't need one in a procedural language. That's because each expression must reduce to a value.
So suppose I write this:
if even n
then n
else n + 1
Then the number will replace the "if," "then," and "else" expressions. And the computer will remember the number until your program terminates.
@@williammcenaney1331 week, if you're going to try, you'll need:
sys.setrecursionlimit
would you argue that a strongly typed language would be more suitable for a beginner learning programming for the first time instead of python?
I absolutely would. I started out with Java and learned a lot of important concepts that helped with Python, if you're more dedicated you can even go lower and start with C. Java was enough for my profession.
what is your code editor? I use Anaconda / Spyder, and do not see that in use here...
That's pycharm
Maybe I'm a beginner but what is the reason to have "main " between""__"" ? Is there any use.
Further more why did you not have error when checking if __name__==__main__
As __name__ as no value affected
__name__ is automatically set to "__main__" when the script is run normally. If it is imported, __name__ is a different value which I'm not entirely sure about.
By default, the name of the Python main module is always "__main__", i.e. when it is being run as a script.
If it were to be imported, the __name__ when become that of the module.
variables with trailing underscores are understood as a special type, and are already declared and initialized when the script runs. '_name_' will adopt the string name of the current module which defaults to '_main_' if the script is being run directly. Otherwise the string will take the name of the import during execution.
so when the script executes and name is not main (in other words the code was imported), the code block to run on compiling will be skipped, and the program will act as an object.
Type annotations saved Python for me. I apparently really rely on type annotations in more complicated code. I was doing type definitions in comments until realized it was an option in the language.
Is the pip mypy also available for vscode?
It is indeed
@@Indently thanks for your fast reply! Btw I have a discord server made for friends who also learn python and your channel have great tips on how to learn python faster. So thank you for your teaching in a way! It is accessible and easy to understand :)
Solarlint extentions give clear check and hints, try it
Everything was solid up to the p for p in people I don't use any single character variables anymore it doesn't help with readability and I'm not trying to minify everything if the goal is to make it readable. I would just use a list comprehension when absolutely necessary.
I agree, but sometimes there's too much redundancy, and the expression is simple enough that's it's obvious at 1st glance what the single-char var is
Regarding Big Functions. It sounds really similar to the argument that you should write all your small functions directly in cython. That way even if performance is not a concern right now those functions can be reused in potential other future parts of the code where performance might matter making your code more reusable.
To that I would say: stop it, get some help.
Just write the code and after it works if you see you have a performance bottleneck or a good reason to split off functions (code duplication or wanting to test only a part of a function) than do the work instead of always doing it prematurely.
Stop optimizing for potential future scenarios at the cost of the present.
My only recommendation is that you plan your code before you write your code. Living in the moment is only advised if your program is as big as printing "hello, world".
Hi, i have a quastion.
Why you use "-> None:" in functions?
" -> type " indicates the type of the return value. When the function doesn't return any value, the annotation must be " -> None "
Hi, could anyone tell me the name of the color scheme used in his ide
For number 1. if __name__ == __main__ is ment to be used in modules that should be run not for testing porpouses. You should test your code with unittest in separate module
Yes, in an ideal world where you also always have all the time you need to make perfect fully documented code.
Here in the real world we need to prioritize stuff. So for code that is most likely only being used by myself, and maybe a few collegues, for a few tasks.
I always put testing in the 'if main' section. The test cases in there also function as a minimum documentation on the intended use of that module.
I do this because here in the real world, insisting on full fledged unit test and other "good practices", only means that test and doc is skipped entirely for such low use code.
Sometimes such code do end up being more useful, and more used. And needs to be upgraded to production ready code (unit test etc) Ask your self "would I prefer to upgrade a module with test cases in the 'if main', or a module where test was done on command line on some unknown pc?"
@@martin_hansen I disagree wih you. if __name__ == "__main__" statement indicates that this module is ment to be used.
And about "real world". I have only 3+ years of proffessional experience with python but in each project (but he last one) we had tests. lots of test. In fact there was much more code for test than for real functionality. It may look like unnecessary work but it is very helpfull and much needed in large projects. On the other hand, I'm working with quite small project without any tests and it is nightmare.
PS. I understand that with your own project you do not unittest (I dont myself) because delivering functionality is more fun :)
I have no argument against people who extensively test their code, you guys are the true heroes who really go out of your way to make your apps reliable, and I respect it.
For some of us though, when we are building modules, we tend to test functionality as we write it. The complete opposite of TDD. Forgetting those function calls in a module you created can be a headache if you don't use the "if name is equal to main" check.
If _name__ == "__main__"
foo()
Does NOT indicate module is ment to be run. It is the exact opposite.
The whole reason of adding this condition is to shield the call to foo() from being called when it is imported by another module.
If the module is intended to just be run and not imported, you should just put
foo()
So the 'if _name__ == "__main__"' statement tells me that this module was intended to be imported but can also be run.
I understand what you're thinking Martin, but if you add an "if name is equal to main check" anywhere in your code, it will tell anyone who sees the script that something was meant to be run directly. Whether it's just for personal testing purposes, or to prevent it from being executed when being imported. It tells whoever is reading it, that there is functionality that can be executed directly in that script.
I would never run "foo()" out in the open like that. You will have to track that call down later if you forget about it when you are importing the module that contains it.
For type annotations, you can also use a .pyi file.
Also if you have the exact use case with the long_names, just use filter()
I think the most important python habit I have is to write as little code as possible in python, because python is a programming language by mathematicians and nowadays data scientist for themselves, and not actual software developers.
@6:22 Since AND is a shortcut operator, you need to rewrite that: return has_id AND age >= 21. If they don't have ID, they can't prove their age. So if has_id is false, the comparison age >= 21 is never checked. The way you wrote it, the comparison would be checked with no proof of age!! 🙂 But seriously, since your premise here is good Python habits, I thought this needed to be mentioned.
pretty good vid.
Not a fan of the "is_bob" function as it has hard coded value in the function name without describing what it does.
I would prefer something like is_blacklisted with a bliacklist: list[str] = ['bob']
inside or someplace where it can be obtained.
Now you don't need to refactor if the name is really "Robert"
is_blacklisted is a much better real world example. It was supposed to be an amusing example where in this world Bob was the only one that would ever be blacklisted.
But yes, in real life, you'd probably have many more people blacklisted than just Bob :)
@@Indently agreed.. I know what you were doing and it was a good point. I was just throwing in some additional contribution.
You'd be surprised (or maybe you wouldn't) at just how bad function naming and structure can be and how it can impact changes to program requirements
For sure :)