avatarYong Cui

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

1469

Abstract

<p id="5d64" type="7">一言蔽之,在考慮所有利息、手續費、服務費、雜費、還款假期、利息回贈、現金回贈、分期供款等花巧東西後,化繁為簡,變為我們最熟悉的那個利率便是「實際年利率」喇!</p><h1 id="6d4d">認識「實際年利率」的好處</h1><p id="3f62">好處只有一個,因為「實際年利率」是一個化繁為簡後的利率,赤條條無遮無掩無得花巧,<b>所以是一個可以用來 apple-to-apple 用來直接比較不同貸款方案利息平貴的 rate!</b> <b>其他所有 rate 什麼手續費什麼月平息基本上都可以掃開喇!</b></p><h1 id="bf34">APR 很好,但要小心別把優惠 double-count!</h1><p id="752a">根據銀行公會的指示,如果銀行為客戶提供現金回贈時,是有責任<b>同時提供</b>「包括」和「不包括」現金回贈的 APR,但在廣告 tagline 時仍然可以選擇只寫其中一個 (當然是抱括現金回贈的那個,因為那個 APR 較低嘛)。</p><p id="99ae">以大新銀行「分期快應錢」做個例子,貸款額 $100 萬的客戶一般可享 $2,000 的現金回贈,以 12 個還款期計算,當考慮這筆 $2,000 回贈時,APR 為 2.08%,不考慮時則升至 2.45%。</p><figure id="a9d1"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*mq63eY3Knbz21nm0RbCoqw.png"><figcaption>source: <a href="http://www.dahsing.com/html/tc/personal_loan/express_money.html">http://www.dahsing.com/html/tc/personal_loan/express_money.html</a></figcaption></figure><figure id="cb60"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*WEIvTX8iHtWCiQ1ZS9cPJg.png"><figcaption>source: <a href="http://www.dahsing.com/tc/pdf/loan/em_T&amp;C_tc.pdf">http://www.dahsing.com/tc/pdf/loan/em_T&amp;C_tc.pdf</a></figcaption></figure><p id="df8b">但當你瀏覽宣傳單張、瀏覽網頁或在分行被銷售的時候,經時會看到 / 聽到類似的話:</p><p id="023a" type="7">好抵架,如果借 $100 萬,APR 低到 2.08%,「仲有」 $2,000 現金回贈添!</p><p id="57dc">留意番,魔鬼就在「仲有」兩隻字嗰度,2.08% 已考慮 $2,000 現金回贈!所以唔應該係「仲有」,而應該係「包括咗」... <b>一個不小心就會把優惠 double-count 了!</b></p><p id="cba2">另外一個可以降低 APR 的方法便是提供「首月還款假期」,即第二個月才開始還款,類似的 tagline 包括:</p><

Options

p id="8889" type="7">好抵架,如果借 $100 萬,APR 低到 2.08%,「仲有」 首月還款假期添!</p><p id="3599">謹記所有優惠也會影響 APR ,<b>分清楚到底廣告/職員說的到底是「優惠前」還是「優惠後」的 APR 就能作出精明選擇了</b></p><p id="166e">版主推介:</p><div id="7d3e" class="link-block"> <a href="https://medium.com/@Watin/%E9%8A%80%E8%A1%8C%E5%B0%8F%E7%9F%A5%E8%AD%98-1-%E8%B2%B8%E6%AC%BE%E5%89%8D%E5%BF%85%E8%A6%81%E6%90%9E%E6%87%82%E7%9A%84-78-%E6%B3%95%E5%89%87-c4fbdc2cd0c3"> <div> <div> <h2>銀行小知識 (1) — 貸款前必要搞懂的「78 法則」</h2> <div><h3>知道了做貸款便有預算了</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*jNn_gXMBUzrq4tf_96JwXA.png)"></div> </div> </div> </a> </div><div id="6ca5" class="link-block"> <a href="https://medium.com/@Watin/%E4%BF%A1%E7%94%A8%E5%8D%A1%E9%96%91%E8%AB%87-11-%E5%B8%B6-2-%E5%BC%B5%E5%85%AB%E9%81%94%E9%80%9A-50b7ca868310"> <div> <div> <h2>信用卡閑談(11) — 如何賺盡八達通回贈?</h2> <div><h3>帶 2 張八達通出街!</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*acfp_LQv6zcOi9ce0R0-Pg.png)"></div> </div> </div> </a> </div></article></body>

Top 5 Mistakes You Make When Declaring Functions in Python

Write better functions

Photo by Ramón Salinero on Unsplash.

Functions are a critical component in any programming project. If done correctly, it’s a practical way to write readable and maintainable code. However, when the functions are not declared correctly, your code becomes hard to read and the long-term maintainability is low — even if we assume that you wrote the code that maintains the same project because any programmer can forget what they did.

Given the importance of functions in programming, Python certainly included, I would like to identify the most common mistakes that some Python programmers can make when declaring functions. By knowing these pitfalls, we can implement the corresponding best practices that will not only improve your code’s readability but also make it more maintainable.

"Readability counts." — The Zen of Python

1. Improper Function Names

Giving names isn’t something that bothers you only when you have a new baby or pet. You may disagree, but as a programmer, it’ll be a challenging task throughout your career. The challenge comes from the standard that we have for function names, which should be unique, informative, and consistent.

Unique

This is a very straightforward requirement. Like with any Python objects, we use names to identify functions. If we declare functions with the same name, either your Python IDE (Integrated Development Environment, such as PyCharm, Visual Studio Code) will complain or the late comer becomes the winner. Consider the following example. We declared two functions named say_hello, and as you can see, when we called the function, the second one that we declared got called:

Informative

Functions are written to perform certain defined operations, and thus their names should reflect their duties. If the names can’t explicitly inform us of these duties, we will struggle to understand other people’s programs or our own code that we wrote last month. Being informative means being specific and accurate to the intended purposes of the functions. Consider the following examples:

Consistent

Python programming encourages modality, which implies that we want to group related classes and functions in certain modules. Within modules and between modules, you want to name your functions consistently. In terms of consistency, we mean that we use the same conventions for particular kinds of objects and operations. Consider the following trivial examples. The first three functions all perform similar operations with two numbers, and thus I use the same format: verb + underscore + numbers. In the custom class Mask, the two functions promotion_price and sales_price have similar name structure, with the first part defining the kind of the price and the second part indicating the nature of the returned value (i.e. a price expressed as a floating-point number).

2. Mixed Duties and Excessive Length

Another common mistake is that a particular function has too many mixed duties — a mistake that even some senior programmers can make sometimes if they don’t refactor their programs continuously. In terms of duties for a given function, the best practice is that the function has only one well-defined duty that can be easily reflected by its sensible name.

Another common accompanying symptom of functions with mixed duties is they tend to be excessively long, and thus it’s harder to understand the functions and debug should any bugs arise. Let’s consider the following hypothetical example. We use the popular pandas library for processing our physiological data collected in our experiment. For each subject, we have four sessions of data in the CSV format. We can write a function called process_physio_data that includes all three data processing steps. However, because of the complexity of the data, the function will be over 100 lines of code.

Instead of writing this long function, we could write the following functions that can better show the steps involved in processing these data. As shown in the code snippet below, we create three helper functions that are respectively responsible for the three steps. Notably, each of these functions has exactly one duty. The updated process_physio_data function is thinner and clearer, and its only duty is to provide a pipeline to process the physiological data. With this refactoring of these jobs, the overall readability of the code is much improved.

3. No Documentation

This is a common mistake from which programmers have to learn their lesson in the long term. At the surface, it seems perfectly fine that your code still runs as it’s supposed to — even if you don’t have any documentation. For example, when you’re working on a single project continuously throughout a few weeks, you know exactly what you’re doing with each function. However, when you have a need to revisit your code to update some features, how much time will you have to spend figuring out what you did? I learned the lesson the hard way and you have probably had similar experiences.

A lack of documentation can be a bigger problem in a teamwork environment where people share APIs or when you’re making open-source libraries. When we use others’ functions, especially complicated ones, we don’t know the specific operations within the functions. However, we simply need to read the documentation to know how to call the function and what the expected return value is. Can you imagine if none of the libraries or frameworks that your work relies on had any documentation?

I’m not saying that you should write extensive docstrings for your functions. I believe that if you have followed the naming standards to keep your function names unique, informative, and consistent and each of your functions performs just one duty and has proper length, you don’t need to write too much documentation for your functions. However, if you work as part of a big team either in a corporate or open-source community, you have to implement standard documentation conventions for your own benefit and for others too.

I’m not going to expand on this topic, but here’s a Medium article about writing good Python docstrings.

4. Incorrect Use of Default Values

When we write functions, Python allows us to set some default values to certain arguments. Many built-in functions use this feature too. Consider the example below. We can create a list object using the range() function, which has the general syntax range(start, stop, step). When it’s omitted, the default step argument will use one. However, we can explicitly set the step argument (say, 2) in the code below:

However, things can become tricky when we write functions involving default values with mutable data types. When we say mutable data, we mean that the Python objects can be changed after their creation, such as lists, dictionaries, and sets. To learn more about Python data mutability, you can refer to my previous article. Consider the following trivial example regarding the use of default values with mutable arguments in functions:

When we’re trying to append the score 98, the expected outcome is printed because we omit the scores argument and we’re expecting the empty list to be used. When we’re trying to append the score 92 to a list of [100, 95], the outcome [100, 95, 92] is also as expected. However, when we’re trying to append the score 94, some of us may expect the outcome to be [94], but it’s not the case. Why can that happen?

It’s all because functions in Python are also first-class citizens and considered regular objects (see my previous article about functions being objects in Python). The implication is that when a function is defined, an object is created, including the function’s default variables. Let’s see a code snippet about these concepts:

We modify the previous function, allowing it to output the memory address for the scores list. As you can see, before we call the function, we’re able to find out the default values of the function’s argument and its memory address accessing the __default__ attribute. After calling the function twice, the same list object with the same memory address has been updated.

What’s the best practice then? We should use None as the default value for the mutable data type, such that the function doesn’t instantiate the mutable object when the function is declared. When the function is called, we can create the mutable object as applicable. See the code below for additional information. Now, everything works as expected:

5. Abuse of *args & **kwargs

Python allows us to write flexible functions by supporting variable numbers of arguments. If you recall, you must have seen *args and **kwargs somewhere in the documentation of certain libraries. In essence, *args refers to an undetermined number of positional arguments, while **kwargs refers to an undetermined number of keyword arguments.

In Python, positional arguments are arguments that are passed based on their positions, while keyword arguments are arguments that are passed based on their specified keywords. Let’s see a trivial example below:

In the function add_numbers, num0 and num1 are positional arguments, while num2 and num3 are keyword arguments. One thing to note is that you can change the order between keyword arguments but not between positional and keyword arguments. Let’s take it a step further by looking at how *args and **kwargs work. It’s best to learn them with an example. Two things to note:

  1. The variable number of positional arguments is handled as a tuple, and thus we can unpack it with one asterisk (learn more about tuple unpacking in this article).
  2. The variable number of keyword arguments is handled as a dictionary, and thus we can unpack it with two asterisks (learn more about dictionary unpacking in this article).

Although the availability of *args and **kwargs allows us to write more flexible Python functions, the abuse of them can lead to some confusion with your function. Earlier, I mentioned that we can use the pandas library for data manipulation and briefly mentioned the read_csv function, which reads a CSV file. Do you know how many arguments this function can take? Let’s see its official documentation:

pandas.read_csv(filepath_or_buffer: Union[str, pathlib.Path, IO[~AnyStr]], sep=',', delimiter=None, header='infer', names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, skipfooter=0, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, cache_dates=True, iterator=False, chunksize=None, compression='infer', thousands=None, decimal: str = '.', lineterminator=None, quotechar='"', quoting=0, doublequote=True, escapechar=None, comment=None, encoding=None, dialect=None, error_bad_lines=True, warn_bad_lines=True, delim_whitespace=False, low_memory=True, memory_map=False, float_precision=None)

If you count them, you’ll find out that the total number of arguments is 49 — one positional and 48 keyword arguments. Theoretically, we can make the list shorter by doing this:

pandas.read_csv(filepath_or_buffer: Union[str, pathlib.Path, IO[~AnyStr]], **kwargs)

However, in the actual implementation of this function, we’ll still have to unpack the **kwargs and figure out how to read the CSV file correctly. Why were these seasoned Python developers willing to list all these keyword arguments? It’s all because they understand the following principle:

“Explicit is better than implicit.” — The Zen of Python

Although using **kwargs could save us some writing for the first line of our function declaration, the cost is that our code becomes less explicit. The same idea also applies to the use of *args. As mentioned above, if we work in a code-sharing environment, we always want our code to be explicit and thus easier to understand. Therefore, whenever possible, we want to avoid using *args and **kwargs to write more explicit code.

Takeaways

In this article, we reviewed five common mistakes that Python programmers can make in their code. Although you can have your own style of coding by overlooking these mistakes in your projects, your code can become hard to understand and result in low long-term maintainability. Therefore, if possible, we all may want to avoid these mistakes and facilitate code readability and thereby shareability.

Additional Reading

In this article, we mentioned several concepts that have been covered by my previous articles, such as functions being objects in Python and unpacking tuples and dictionaries. If you’re interested, please feel free to read some of them. For your convenience, the links are provided below:

Programming
Technology
Artificial Intelligence
Software Engineering
Python
Recommended from ReadMedium