当前位置: 首页 > news >正文

require函数

require函数

https://www.cnblogs.com/sylvan/p/8592472.html

Lua笔记——4.Package

目录

  • require函数
    • 创建模块
    • 调用模块
  • module函数
    • 简化module的创建
    • Lua5.1 & setfenv (f, table)
    • Lua5.1 & module(...)
    • Lua5.2之后

 


module简介

Lua 5.1 加入模块管理机制module,类似于Java的packages、C++的namespaces,可以通过require用于加载模块,module用于创建模块。require加载一个自定义或者第三方的module,然后便得到了一个全局变量,表示一个table。

Lua 5.2 之后则去掉了module创建模块的函数,仅保留requir加载函数在全局环境

回到顶部

require函数

创建模块

在Lua中创建一个模块最简单的方法:创建一个table,并将所有需要导出的函数放入其中,最后返回这个table,相当于将导出的函数作为table的一个字段。

创建一个简单模块,代码如下:

--file: simpleMod.lua

local _Mod = {}

_Mod._Mod = _Mod

function _Mod:New(name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    return setmetatable(mod , {__index = self})
end

function _Mod:Update()
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

return _Mod

调用模块

require (modname)

Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. If it is, then require returns the value stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.

To find a loader, require is guided by the package.loaders array.(package.loaders array:A table used by require to control how to load modules.) By changing this array, we can change how require looks for a module. The following explanation is based on the default configuration for package.loaders.

First require queries package.preload[modname].(A table to store loaders for specific modules) If it has a value, this value (which should be a function) is the loader. Otherwise require searches for a Lua loader using the path stored in package.path. If that also fails, it searches for a C loader using the path stored in package.cpath. If that also fails, it tries an all-in-one loader (see package.loaders).

Once a loader is found, require calls the loader with a single argument, modname. If the loader returns any value, require assigns the returned value to package.loaded[modname]. If the loader returns no value and has not assigned any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the final value of package.loaded[modname].

If there is any error loading or running the module, or if it cannot find any loader for the module, then require signals an error.

require函数的调用形式为require "模块名"

加载给定的模块名modname,require函数首先会在表package.loaded中查看是否已经加载过,如果已经加载过,则会返回储存在package.loaded[modname]中的模块,否则,require函数将会尝试为此模块寻找一个加载器。

require函数将会被package.loader的数组引导来寻找适用于该模块的加载器,(package.loaders,是一个被require函数用来控制如何加载模块的表或者说数组)我们可以自己改变require函数寻找模块的方法,下面是package.loaders的默认配置的下的解释:

首先,require函数会查看package.preload[modname](用来储存特定模块加载器的表),如果有值(该值应是一个函数),则改值就是要找的加载器。否则,require函数将会通过储存在package.path中的路径来寻找一个Lua 加载器。如果也失败了,requier函数则会通过储存在package.cpath中的路径来寻找一个C 加载器。如果也失败了,require函数将会尝试使用一个package.loaders中的通用的加载器——all-in-one 加载器。

一旦找到加载器,require函数会传递一个单一的参数模块名modname到这个加载器,如果加载器有任何的返回值,require函数会将返回的值连同模块名modname会注册到表 package.loaded[modname]中。如果加载器没有返回值并且还没有任何值连同模块名modname会注册到表 package.loaded[modname]中,那么require函数将会注册 true 到该键值对的入口。任何情况下,require 函数都会返回表 package.loaded[modname]最终的值。

如果在加载或者运行该模块,亦或者完全没有找到该模块的加载器,则require函数会显示error。

将上方simpleMod.lua放置在当前目录的子目录Util下,调用代码:

--file: testMod.lua

print("Before the require function , packages in the package.loaded :")
for k in pairs(package.loaded) do print(k) end

print("package.preload loader number : "..#package.preload)

--lua5.1中的package.loaders 在lua5.2之后版本中更名为package.searchers
--所以使用package.loaders or package.searchers来兼容版本
for k,v in pairs(package.loaders or package.searchers) do print("loader : "..k .. "   "..tostring(v)) end

--require function ,PS: The Util Is A Subdirectory of current directory
local Mod = require "Util.simpleMod"

print("\nAfter the require function , the table package.loaded")
for k in pairs(package.loaded) do print(k) end

--simpleMod's usage code
local mod = Mod:New("TestFeature")
mod:Update()

print("\nModFeature Name : "..mod.Name .. "\nVersion : "..mod._VERSION)

输出结果:

 

回到顶部

module函数

简化module的创建

  • require会将模块名modname传递给loader,在loader加载模块时,我们可以在模块中接收传递的模块名

  • 有时我们会漏写创建模块最后的return语句,我们可以将所有与模块创建相关的设置任务都集中在开头,
    消除return语句的一种方法是,loader加载模块时将模块名modname以及模块注册至表package.loaded[modname]中

代码如下:

--file: simpleMod.lua

local _Mod = {}
_Mod._Mod = _Mod

local modname = ...
_G[modname] = _mod
package.loaded[modname] = _Mod

function _Mod:New(name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    return setmetatable(mod , {__index = self})
end

function _Mod:Update()
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

Lua5.1 & setfenv (f, table)

Sets the environment to be used by the given function. f can be a Lua function or a number that specifies the function at that stack level: Level 1 is the function calling setfenv. setfenv returns the given function.

As a special case, when f is 0 setfenv changes the environment of the running thread. In this case, setfenv returns no values.

当我们在创建模块或者访问同一个模块中的其它函数时,需要限定名称,就比如上面代码中的_Mod,为此,我们可以让模块的主程序块有一个独立的环境,这样不仅它的所有函数都可共享这个table,而且它的所有全局变量也都记录在这个table中。而模块所要做的就是将这个table赋予模块名和package.loaded。

这样,我们在调用同一个模块中的函数new时,也不用指定m了。在写自己的模块时,就省去了前缀;但是与此同时,当我们调用setfenv(1,m)函数之后,会将一个空table m作为环境,但是这样之后就无法访问前一个环境中全局变量了(例如setmetatablet之类的全局变量)

解决方法:
方法一:在调用setfenv(1,m)之前,为m设置元表,使元表的__index域指向_G,代码如下:

--file: simpleMod.lua

local _Mod = {}
_Mod._Mod = _Mod

local modname = ...
_G[modname] = _mod
package.loaded[modname] = _Mod

--Before calling the func setfenv() ,set the mestatable {__index = _G} to the _mod
setmetatable(_Mod,{__index = _G})
setfenv(1,_Mod)

function New(self,name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    return setmetatable(mod , {__index = self})
end

function Update(self)
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

方法二:在调用setfenv(1,m)之前,使用局部变量将全局变量_G保存起来,代码如下:

--file: simpleMod.lua

local _Mod = {}
_Mod._Mod = _Mod

local modname = ...
_G[modname] = _mod
package.loaded[modname] = _Mod

--Before calling the func setfenv() ,storage _G to the local variable
local _G = _G
setfenv(1,_Mod)

function New(self,name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    --This way to use the variables in the _G table
    return _G.setmetatable(mod , {__index = self})
end

function Update(self)
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

方法三:在调用setfenv(1,m)之前,只将需要使用的全局变量保存起来,代码如下:

--file: simpleMod.lua

local _Mod = {}
_Mod._Mod = _Mod

local modname = ...
_G[modname] = _mod
package.loaded[modname] = _Mod

--Before calling the func setfenv() ,storage useful variable the local variable
local setmetatable = setmetatable
setfenv(1,_Mod)

function New(self,name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    --This way to use the variables in the _G table
    return setmetatable(mod , {__index = self})
end

function Update(self)
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

Lua5.1 & module(...)

在Lua 5.1中,可以用module(...)的函数来代替以下代码:

-- local _Mod = {}
-- _Mod._Mod = _Mod

-- local modname = ...
-- _G[modname] = _mod
-- package.loaded[modname] = _Mod

-- setfenv(1,_Mod)

由于在默认情况下,module不提供外部访问,必须在调用它之前,为需要访问的外部函数或模块声明适当的局部变量。然后Lua提供了一种更为方便的实现方式,即在调用module函数时,多传入一个package.seeall的参数,相当于 setmetatable(_Mod, {__index = _G}):

module(...,package.seeall)

完整代码:

--file: simpleMod.lua

-- local _Mod = {}
-- _Mod._Mod = _Mod

-- local modname = ...
-- _G[modname] = _mod
-- package.loaded[modname] = _Mod

--Before calling the func setfenv() ,set the mestatable {__index = _G} to the _mod
-- setmetatable(_Mod,{__index = _G})
-- setfenv(1,_Mod)

module(...,package.seeall)

function New(self,name)
    local mod = {}
    mod.Name = name or "Default Name"
    mod._VERSION = 0.01
    --This way to use the variables in the _G table
    return setmetatable(mod , {__index = self})
    end

    function Update(self)
    self._VERSION = self._VERSION + 0.01
    return self._VERSION
end

Lua5.2之后

  • Function module is deprecated. It is easy to set up a module with regular Lua code. Modules are not expected to set global variables.

  • Functions setfenv and getfenv were removed, because of the changes in environments.

  • module函数被抛弃。用普通的Lua代码就可以很容易的创建模块。而模块也不需要去设置全局变量。

  • setfenv以及getfenv函数被移除,因为会对环境产生改变。

Lua5.2之后,如果require引入使用module声明和定义的模块就会报错

REF

http://lua-users.org/wiki/

http://www.lua.org/manual/5.1/manual.html#5.3

http://www.lua.org/manual/5.2/manual.html#pdf-package.searchers

https://www.runoob.com/manual/lua53doc/manual.html#pdf-require

http://www.jb51.net/article/55818.htm

https://moonbingbing.gitbooks.io/openresty-best-practices/lua/not_use_module.html

https://www.cnblogs.com/zsb517/p/6822870.html

 

 

相关文章:

  • UE4 给static mesh 动态添加Socket
  • UE4 角色用Child Actor组建添加装备 这样方便随时添加,更换套装等行为
  • Unity Assets目录下的特殊文件夹名称
  • Unity-ShaderVariantCollection
  • Unity渲染教程(九):复杂材质 https://www.jianshu.com/p/5e3af869870f
  • HttpWebRequest(System.Net)模拟HTTP发送POST
  • C#中用HttpWebRequest中发送GET/HTTP/HTTPS请求 (转载)
  • System.Net.HttpWebRequest.GetRequestStream超时问题
  • System.Net.HttpWebRequest.GetResponse() 远程服务器
  • 【转载】HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法
  • UE4 AIController
  • [UE4]创建自定义AIController的方法(C++)
  • eclipse + pydev远程调试OpenStack
  • 调用shell jenkins不能自动结束
  • Unreal 第三方 Python平台
  • 〔开发系列〕一次关于小程序开发的深度总结
  • 10个确保微服务与容器安全的最佳实践
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • DOM的那些事
  • javascript从右向左截取指定位数字符的3种方法
  • Java精华积累:初学者都应该搞懂的问题
  • JS题目及答案整理
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • mockjs让前端开发独立于后端
  • React Native移动开发实战-3-实现页面间的数据传递
  • supervisor 永不挂掉的进程 安装以及使用
  • 动态规划入门(以爬楼梯为例)
  • 分享几个不错的工具
  • 服务器之间,相同帐号,实现免密钥登录
  • 给新手的新浪微博 SDK 集成教程【一】
  • 简析gRPC client 连接管理
  • 手机端车牌号码键盘的vue组件
  • 一、python与pycharm的安装
  • 用Canvas画一棵二叉树
  • 用Visual Studio开发以太坊智能合约
  • 鱼骨图 - 如何绘制?
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • # 数论-逆元
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (16)Reactor的测试——响应式Spring的道法术器
  • (175)FPGA门控时钟技术
  • (java)关于Thread的挂起和恢复
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • ./configure,make,make install的作用
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET值类型变量“活”在哪?
  • @Builder用法
  • [ linux ] linux 命令英文全称及解释
  • [20161101]rman备份与数据文件变化7.txt
  • [2024最新教程]地表最强AGI:Claude 3注册账号/登录账号/访问方法,小白教程包教包会
  • [APIO2012] 派遣 dispatching