Referece: https://realpython.com/blog/python/inner-functions-what-are-they-good-for/

Let’s look at three common reasons for writing inner functions.

Remember: In Python, a function is a “first-class” citizen, meaning they are on par with any other object (i.e., integers, strings, lists, modules, etc.). You can dynamically create or destroy them, pass them to other functions, return them as values, and so forth.

This tutorial utilizes Python version 3.4.1.

1. Encapsulation

You use inner functions to protect them from anything happening outside of the function, meaning that they are hidden from the global scope.

Here’s a simple example that highlights that concept:

def outer(num1):
def inner_increment(num1): # hidden from outer code
return num1 + 1
num2 = inner_increment(num1)
print(num1, num2) inner_increment(10)
# outer(10)

Try calling the inner_increment() function:

Traceback (most recent call last):
File "inner.py", line 7, in <module>
inner_increment()
NameError: name 'inner_increment' is not defined

Now comment out the inner_increment call and uncomment the outer function call, outer(10), passing in 10 as the argument:

10 11

Keep in mind that this is just an example. Although this code does achieve the desired result, it’s probably better to make the inner_increment()function a top-level “private” function using a leading underscore: _inner_increment().

The following recursive example is a slightly better use case for a nested function:

def factorial(number):

    # error handling
if not isinstance(number, int):
raise TypeError("Sorry. 'number' must be an integer.")
if not number >= 0:
raise ValueError("Sorry. 'number' must be zero or positive.") def inner_factorial(number):
if number <= 1:
return 1
return number*inner_factorial(number-1)
return inner_factorial(number) # call the outer function
print(factorial(4))

Test this out as well. One main advantage of using this design pattern is that by performing all argument checking in the outer function, you can safely skip error checking altogether in the inner function.

For a more detailed discussion of recursion see, Problem Solving with Algorithms and Data Structures.

2. Keepin’ it DRY

Perhaps you have a giant function that performs the same chunk of code in numerous places. For example, you might write a function which processes a file, and you want to accept either an open file object or a file name:

def process(file_name):
def do_stuff(file_process):
for line in file_process:
print(line)
if isinstance(file_name, str):
with open(file_name, 'r') as f:
do_stuff(f)
else:
do_stuff(file_name)

Again, it is common to just make do_stuff() a private top-level function, but if you want to hide it away as an internal function, you can.

How about a practical example?

Let’s say you want to know the number of WiFi hot spots in New York City. And yes the city has the raw data to tell us: datasource. Visit the site and download the CSV.

def process(file_name):

    def do_stuff(file_process):
wifi_locations = {} for line in file_process:
values = line.split(',')
# Build the dict, and increment values
wifi_locations[values[1]] = wifi_locations.get(values[1], 0) + 1 max_key = 0
for name, key in wifi_locations.items():
all_locations = sum(wifi_locations.values())
if key > max_key:
max_key = key
business = name
print('There are {0} WiFi hot spots in NYC and {1} has the most with {2}.'.format(
all_locations, business, max_key)) if isinstance(file_name, str):
with open(file_name, 'r') as f:
do_stuff(f)
else:
do_stuff(file_name) process("NAME_OF_THE.csv")

Run the function:

There are 1251 WiFi hot spots in NYC and Starbucks has the most with 212.

3. Closures and Factory Functions

Now we come to the most important reason to use inner functions. All of the inner function examples we’ve seen so far have been ordinary functions that merely happened to be nested inside another function. In other words, we could have defined these functions in another way (as discussed); there is no specific reason for why they should be nested.

But when it comes to closure, that is not the case: You must utilize nested functions.

What’s a closure?

A closure simply causes the inner function to remember the state of its environment when called. Beginners often think that a closure is the inner function, when it’s really caused by the inner function. The closure “closes” the local variable on the stack and this stays around after the the stack creation has finished executing.

An example

def generate_power(number):
"""
Examples of use: >>> raise_two = generate_power(2)
>>> raise_three = generate_power(3)
>>> print(raise_two(7))
128
>>> print(raise_three(5))
243
""" # define the inner function ...
def nth_power(power):
return number ** power
# ... which is returned by the factory function return nth_power

What’s happening here?

  1. The ‘generate_power()’ function is a factory function – which simply means that it creates a new function each time it is called and then returns the newly created function. Thus, raise_two and raise_three are the newly created functions.
  2. What does this new, inner function do? It takes a single argument, power, and returns number**power.
  3. Where does the inner function get the value of number from? This is where the closure comes into play: nth_power() gets the value of power from the outer function, the factory function. Let’s step through this process:

    • Call the outer function: generate_power(2)
    • Build the nth_power() function which takes a single argument power
    • Take a snapshot of the state of nth_power() which includes power=2
    • Pass that snapshot into the generate_power() function
    • Return the nth_power() function

    Put another way, the closure functions to “initialize” the number bar in the nth_power()function and then returns it. Now, whenever you call that newly returned function, it will always see its own private snapshot that includes power=2.

Real World

How about a real world example?

def has_permission(page):
def inner(username):
if username == 'Admin':
return "'{0}' does have access to {1}.".format(username, page)
else:
return "'{0}' does NOT have access to {1}.".format(username, page)
return inner current_user = has_permission('Admin Area')
print(current_user('Admin')) random_user = has_permission('Admin Area')
print(current_user('Not Admin'))

This is a simplified function to check if a certain user has the correct permissions to access a certain page. You could easily modify this to grab the user in session to check if they have the correct credentials to access a certain route. Instead of checking if the user is just equal to ‘Admin’, you could query the database to check the permission then return the correct view depending on whether the credentials are correct or not.

Conclusion

The use of closures and factory functions is the most common, and powerful, use for inner functions. In most cases, when you see a decorated function, the decorator is a factory function which takes a function as argument, and returns a new function which includes the old function inside the closure. Stop. Take a deep breath. Grab a coffee. Read that again.

Put another way, a decorator is just syntactic sugar for implementing the process outlined in the generate_power() example.

I’ll leave you with an example:

def generate_power(exponent):
def decorator(f):
def inner(*args):
result = f(*args)
return exponent**result
return inner
return decorator @generate_power(2)
def raise_two(n):
return n print(raise_two(7)) @generate_power(3)
def raise_three(n):
return n print(raise_two(5))

If your code editor allows it, view the generate_power(exponent) and generate_power(number)functions side-by-side to illustrate the concepts discussed. (Sublime Text has Column View, for example).

If you have not coded the two functions, open the code editor and start coding. For new programmers, coding is a hands on activity, like riding a bike you just have to do it – and do it solo. So back to the task at hand. After you typed the code, you can now clearly see that the code is similar in that it produces the same results but there are differences. For those who have never used decorators, noting these differences will be the first step in understanding them if you venture down that path.


If you’d like to know more about this syntax and decorators in general, check out our Primer on Python Decorators. Comment below with questions.

Edits made by Derrick Kearney. Thank you!

Inner Functions - What Are They Good For?的更多相关文章

  1. asp.net MVC helper 和自定义函数@functions小结

    asp.net Razor 视图具有.cshtml后缀,可以轻松的实现c#代码和html标签的切换,大大提升了我们的开发效率.但是Razor语法还是有一些棉花糖值得我们了解一下,可以更加强劲的提升我们 ...

  2. 【跟着子迟品 underscore】Array Functions 相关源码拾遗 & 小结

    Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中. 阅读一些著名框架类库的源码,就好像和一个个大师对 ...

  3. 【跟着子迟品 underscore】Object Functions 相关源码拾遗 & 小结

    Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中. 阅读一些著名框架类库的源码,就好像和一个个大师对 ...

  4. ajax的使用:(ajaxReturn[ajax的返回方法]),(eval返回字符串);分页;第三方类(page.class.php)如何载入;自动加载函数库(functions);session如何防止跳过登录访问(构造函数说明)

    一.ajax例子:ajaxReturn("ok","eval")->thinkphp中ajax的返回值的方法,返回参数为ok,返回类型为eval(字符串) ...

  5. QM模块包含主数据(Master data)和功能(functions)

    QM模块包含主数据(Master data)和功能(functions)   QM主数据   QM主数据 1 Material   Master MM01/MM02/MM50待测 物料主数据 2 Sa ...

  6. jQuery String Functions

    In today's post, I have put together all jQuery String Functions. Well, I should say that these are ...

  7. 2-4. Using auto with Functions

    在C++14中允许使用type deduction用于函数参数和函数返回值 Return Type Deduction in C++11 #include <iostream> using ...

  8. [Python] Pitfalls: About Default Parameter Values in Functions

    Today an interesting bug (pitfall) is found when I was trying debug someone's code. There is a funct ...

  9. Kernel Functions for Machine Learning Applications

    In recent years, Kernel methods have received major attention, particularly due to the increased pop ...

  10. Execution Order of Event Functions

    In Unity scripting, there are a number of event functions that get executed in a predetermined order ...

随机推荐

  1. [think]关于个人发展值得记住的一些建议 听别人的话,即使你不想听 不要只做不想 成功不能被复制,但失败总在不停复制。看看别人是怎么倒下的,你可以更早地成功

    [think]关于个人发展值得记住的一些建议 偶然看到一篇采访周爱民的文章,里面的一些建议虽然朴实无华,却感觉很有道理,特此记录: 记者:对于程序员的技术发展和职业规划能否给大家一些建议呢?----- ...

  2. 为Magento1.5新增会员注册字段(转)

    第一步.新建一个模块,在app/etc/modules/目录下新建文件Shuishui_Customer.xml <config> <modules> <Shuishui ...

  3. Android tesseract-orc之扫描身份证号码

    踩了不少坑,终于把这个扫描版的身份证识别做出来了,图片识别引擎用的是tesseract,在已经训练好样本的情况下,感觉识别率还是一般般~ 下面说一说大概几个坑. 一. 编译tesseract-orc ...

  4. MongoDB 学习笔记(1)

    数据库 一个mongodb中可以建立多个数据库. MongoDB的默认数据库为"db",该数据库存储在data目录中. MongoDB的单个实例可以容纳多个独立的数据库,每一个都有 ...

  5. ORA-65179: cannot keep datafiles for a pluggable database that is not unplugged

    SQL> drop pluggable database pdb2; drop pluggable database pdb2 * ERROR at line : ORA-: cannot ke ...

  6. Redis快速入门及应用

    Redis的使用难吗?不难,Redis用好容易吗?不容易.Redis的使用虽然不难,但与业务结合的应用场景特别多.特别紧,用好并不容易.我们希望通过一篇文章及Demo,即可轻松.快速入门并学会应用.一 ...

  7. Linux内核同步:自旋锁

    linux内核--自旋锁的理解 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对于UP系统,自旋锁仅做抢占和中断操作,没有实现真正的“自旋”.如果配置了CON ...

  8. HDU2594 Simpsons’ Hidden Talents 【KMP】

    Simpsons' Hidden Talents Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java ...

  9. android surfaceView 的简单使用 画图,拖动效果

    前面说到了画图,其实更好的就是使用 surfaceView了. surfaceView 继承于 View,View里面嵌套了一个专门用于画图的 surface, 对于一个View的onDraw()方法 ...

  10. 集群扩容的常规解决:一致性hash算法

    写这篇博客是因为之前面试的一个问题:如果memcached集群需要增加机器或者减少机器,那么其他机器上的数据怎么办? 最后了解到使用一致性hash算法可以解决,下面一起来学习下吧. 声明与致谢: 本文 ...