自由学习记录(24)

lua一些散杂疑惑

大G是增加可以通过字符串实现继承的功能

元表里面用别的表里的变量,如果自己这个表里没有这个变量,当然是用别人的,但如果自己的表里有了这个变量,就优先用自己的

如果 元表的self[调用的键] 查无此"变量",那会返回nil,不报错

Lua 复制代码
Object = {} -- 创建一个表 Object

function Object:new(o) -- 定义一个方法 new
    o = o or {}       -- 如果没有传入对象,则创建一个新表
    setmetatable(o, self) -- 设置新表的元表为 Object 本身
    self.__index = self   -- 确保未找到的字段从 Object 中查找
    return o              -- 返回新创建的对象
end

-- 使用 Object:new 创建一个实例
local instance = Object:new()
print(instance)           -- 输出一个 table

function Object:new() 并不是在创建一个变量叫 Object,而是为 Object 表定义了一个方法 new

在 Lua 中:

  • 函数本质上是一种值,就像数字、字符串或表一样。
  • 当函数作为一个表的字段时,这个函数就成为该表的一个"方法"。

一个名对应一个变量,函数或表都算一个变量

面向对象

在 Lua 中,面向对象(OOP)的概念是通过元表和函数来实现的。虽然 Lua 本身并没有内置的 OOP 机制,但它提供了足够灵活的元表和表结构,使得实现封装、继承和多态等 OOP 特性成为可能。

  • 封装:隐藏对象的内部实现,仅通过公开的接口访问数据。
  • 继承:一个对象可以继承另一个对象的属性和方法。
  • 多态:通过相同的接口实现不同的行为。

简单的对象和方法

Lua 复制代码
-- 定义类
local MyClass = {}
MyClass.__index = MyClass  -- 设置元表

-- 构造函数
function MyClass:new(value)
    local obj = setmetatable({}, self)  -- 创建对象并绑定元表
    obj.value = value or 0             -- 初始化属性
    return obj
end

-- 定义方法
function MyClass:getValue()
    return self.value
end

function MyClass:setValue(value)
    self.value = value
end

-- 使用类
local obj = MyClass:new(10)  -- 创建对象
print(obj:getValue())        -- 输出:10
obj:setValue(20)
print(obj:getValue())        -- 输出:20

封装私有属性

Lua 复制代码
local MyClass = {}

function MyClass:new(value)
    local obj = {}           -- 对象
    local privateValue = value or 0  -- 私有属性

    -- 公有方法
    function obj:getValue()
        return privateValue
    end

    function obj:setValue(value)
        privateValue = value
    end

    return obj
end

-- 使用类
local obj = MyClass:new(10)
print(obj:getValue())  -- 输出:10
obj:setValue(20)
print(obj:getValue())  -- 输出:20
-- print(obj.privateValue)  -- 错误:无法直接访问私有属性

还挺巧妙的,把函数变量声明在myclass的new方法里面,通过不local的全局可访问能力达到public的效果(但如果止步于此,等这个函数死了之后,里面的的变量还是会死,所以return obj),而闭包则可以让存了setvalue,getvalue这两个方法的obj返回,里面的单独设置的local变量,则被obj的两个方法形成了新的束缚,所以一直活在new方法里面,但是产生不了可以直接点出的联系

完整的一次封装

Lua 复制代码
-- 定义基类
local Animal = {}
Animal.__index = Animal  --如果 __index 是一个表,Lua 会在这个表中查找键
                         --如果 __index 是一个函数,Lua 会调用这个函数并将表和键作为参数传入
--通过 Animal.__index = Animal,我们让 Animal 表可以在对象的元表中作为 __index 的值。
--当对象无法找到某个键时,Lua 就会在 Animal 表中查找。

-- 构造函数
function Animal:new(name)
    local obj = setmetatable({}, self)
    obj.name = name or "Unnamed"
    return obj
end

-- 定义方法
function Animal:speak()
    print(self.name .. " makes a sound.")
end

---------------------------------------------------
-- 定义子类
local Dog = setmetatable({}, {__index = Animal})  -- Dog 继承自 Animal
Dog.__index = Dog

-- 构造函数(重写)
function Dog:new(name, breed)
    local obj = Animal.new(self, name)  -- 调用基类构造函数
    obj.breed = breed or "Unknown"
    return obj
end

-- 定义方法(重写)
function Dog:speak()
    print(self.name .. " barks.")
end

-- 定义方法(扩展)
function Dog:getBreed()
    return self.breed
end
---------------------------------------------------
-- 使用基类
local animal = Animal:new("Generic Animal")
animal:speak()  -- 输出:Generic Animal makes a sound.

-- 使用子类
local dog = Dog:new("Buddy", "Golden Retriever")
dog:speak()     -- 输出:Buddy barks.
print(dog:getBreed())  -- 输出:Golden Retriever

元表

元表的实际用途是模拟面向对象编程 ,使用元表来实现类的继承对象方法

重载操作:定义自定义的算术或比较操作符行为。

表的默认值:使用 __index 为表设置默认值。

元表是为表设计 的,不能直接为其他类型(如数字、字符串)设置元表

如果需要为字符串或数字定义行为,可以通过 元表代理 来实现。

使用元表时,请确保设计的行为符合预期,避免不必要的复杂性。

__call:定义表作为函数调用时的行为。

__tostring:定义表被 tostring 转换时的行为。

__len:定义 # 运算符的行为(获取长度)。

__index:控制表的"读取"操作。

如果访问的键不存在,则会调用元表中的 __index 方法。

__newindex:控制表的"写入"操作。

如果试图 向表中写入不存在的键,则触发 __newindex。

__index 是一张表时,Lua 会在这个表中查找访问的键。

Lua 复制代码
local defaults = {x = 10, y = 20}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)  -- 输出:10
print(point.z)  -- 输出:nil,因为 `defaults` 中没有 `z`

通过 __index,可以为表设置默认值。当键不存在时返回默认值,而不报错或返回 nil

Lua 复制代码
local defaults = {x = 0, y = 0}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)  -- 输出:0
print(point.y)  -- 输出:0

使用 __index 函数,可以在键访问时动态计算返回值。

Lua 复制代码
local myTable = {}
local mt = {
    __index = function(t, key)
        return "Dynamic value for key: " .. key
    end
}
setmetatable(myTable, mt)

print(myTable.abc)  -- 输出:Dynamic value for key: abc
print(myTable.xyz)  -- 输出:Dynamic value for key: xyz

通过 __index,可以模拟对象的继承关系。 同时,也可以代理访问

Lua 复制代码
-- 父类
local Parent = {name = "Parent"}
function Parent:sayHello()
    print("Hello from " .. self.name)
end

-- 子类
local Child = {name = "Child"}
setmetatable(Child, {__index = Parent})

Child:sayHello()  -- 输出:Hello from Child
Lua 复制代码
local source = {a = 1, b = 2}
local proxy = {}
setmetatable(proxy, {__index = source})

print(proxy.a)  -- 输出:1(从 `source` 中获取)
print(proxy.b)  -- 输出:2(从 `source` 中获取)

如果__index 是表,并且该表本身也有元表,Lua 会递归查找

__index的键如果在对象表里已经有了,

就不用在传入的那个表里找是否存在这个键,然后递归下去了

Lua 复制代码
local grandparent = {x = 1}
local parent = {}
setmetatable(parent, {__index = grandparent})
local child = {}
setmetatable(child, {__index = parent})

print(child.x)  -- 输出:1(从 `grandparent` 中获取)

rawget绕过元表,尝试获取对象表里的值,没有找到就返回nil

rawset则绕过元表,同理修改

Lua 复制代码
local defaults = {x = 10}
local point = {}
setmetatable(point, {__index = defaults})

print(point.x)        -- 输出:10
print(rawget(point, "x")) -- 输出:nil(`point` 本身没有 `x`)

一个综合的例子

Lua 复制代码
local defaults = {x = 0, y = 0}
local point = {}

local mt = {
    __index = function(t, key)
        -- 优先从默认值中查找
        if defaults[key] then
            return defaults[key]
        end
        -- 动态生成其他值
        return "Dynamic-" .. key
    end
}

setmetatable(point, mt)

print(point.x)  -- 输出:0(默认值)
print(point.y)  -- 输出:0(默认值)
print(point.z)  -- 输出:Dynamic-z

__eq:定义相等 (==)

__lt:定义小于 (<)

__le:定义小于等于 (<=)

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 20}

local mt = {
    __lt = function(a, b)
        return a.value < b.value
    end,
    __eq = function(a, b)
        return a.value == b.value
    end
}

setmetatable(t1, mt)
setmetatable(t2, mt)

print(t1 < t2)  -- 输出:true
print(t1 == t2) -- 输出:false

元表本质上是一张表,它可以定义一组元方法,用来控制对表的某些操作的行为

元表的核心机制是通过元方法来实现的,元方法是元表中的特定键(如 __add、__index 等)

setmetatable(table, metatable)

为 table 设置元表为 metatable

getmetatable(table)

获取一个表的元表,如果没有设置元表,则返回 nil

__add:定义加法 (+)

__sub:定义减法 (-)

__mul:定义乘法 (*)

__div:定义除法 (/)

__mod:定义取模 (%)

__pow:定义幂运算 (^)

Lua 复制代码
local t1 = {value = 10}
local t2 = {value = 20}

local mt = {
    __add = function(a, b)
        return {value = a.value + b.value}
    end
}

setmetatable(t1, mt)
setmetatable(t2, mt)

local result = t1 + t2
print(result.value)  -- 输出:30

表之间的加减乘除默认会报错

定义加号,需要传入两个参数,如果是要两个表里的age相加,那么里面必须要有age

运算符只有小于和小于等于,官方没给别的,要自己取反

--concat是..的重写

协程

wrap创造的协程不返回是否执行成功

协程的yield可以返回值,然后用多个变量去接收

第一个默认为是否执行成功true 或者false

在 Lua 中,coroutine.wrap 是用于创建协同程序的一种方式,它与 coroutine.create 类似,但有一些显著的区别和特点。

coroutine.wrap 接收一个函数,返回一个新的函数(不是协同程序)。当调用这个返回的函数时,实际上是启动或恢复对应的协同程序,并返回协同程序运行的结果。

协程的本质是一个线程对象

加了local的变量不会存到大G表里

require()

在 Lua 中,require 是一个用于++加载和运行模块的函数++ ,广泛用于组织代码和++实现模块化开发++ 。以下是关于 require 的详细讲解:


1. 基本作用

require 用于加载 Lua 脚本或 C 模块,并++执行加载的代码++ 。如果模块++已经加载过一次++ ,require 会直接++返回加载的结果++ ,而++不会重复加载++。

-- example.lua 文件

local M = {}

M.greet = function()

print("Hello, Lua!")

end
return M

-- 主脚本 main.lua
local example = require("example")

example.greet() -- 输出:Hello, Lua!

  • require("example") 加载 example.lua 文件。
  • 加载成功后返回文件中 return 的值。

2. 加载机制

require 的加载行为由 Lua 的 ++package.pathpackage.cpath 决定++。它们定义了 Lua 和 C 模块的搜索路径:

  • package.path : 用于搜索 Lua 文件的路径,例如++.lua.luac++文件。
  • package.cpath : 用于搜索 C 模块的路径,例如++动态链接库(.dll.so 等)++。
搜索路径示例
复制代码
print(package.path)
-- 输出类似:
-- ./?.lua;./?/init.lua;/usr/local/share/lua/5.1/?.lua;...

print(package.cpath)
-- 输出类似:
-- ./?.so;/usr/local/lib/lua/5.1/?.so;...

说明

  • ? 是占位符,会被替换为模块名。例如,require("example") 会尝试加载 ./example.lua./example/init.lua
  • 如果路径中找不到模块,会报错:module 'example' not found

3. 避免重复加载

Lua 内部维护了一个全局表 package.loaded ,用来记录已经加载的模块。每次调用 require 时,Lua 会先检查这个表,只有++模块尚未加载时才会重新加载++。

复制代码
local module1 = require("example")
local module2 = require("example")

-- module1 和 module2 是同一个表对象
print(module1 == module2) -- 输出:true

4. 模块结构

为了更好地组织模块,++Lua 社区约定了标准的模块结构++。一个模块通常:

  • 定义为一个表。
  • 包含需要暴露的功能。
  • 在文件末尾++使用 return 返回这个表++。
示例:简单模块
复制代码
-- mymodule.lua
local M = {}

M.add = function(a, b)
    return a + b
end

M.sub = function(a, b)
    return a - b
end

return M

-- main.lua
local mymodule = require("mymodule")

print(mymodule.add(5, 3)) -- 输出:8

print(mymodule.sub(5, 3)) -- 输出:2


5. 加载非 Lua 文件

require 也可以加载 C 模块,通常是通过动态链接库实现的。例如,加载一个 example.so 文件:

复制代码
local example = require("example")  -- 会从 package.cpath 搜索路径加载

需要注意的是,C 模块需要定义特定的入口点,以便 Lua 调用。


6. 自定义 require 的搜索路径

可以通过修改 package.pathpackage.cpath 来自定义 require 的搜索路径。例如:

复制代码
package.path = package.path .. ";./custom/?.lua"

local customModule = require("mymodule")

上述代码会让 require 搜索 ./custom/mymodule.lua


7. 动态加载模块

如果只想加载模块而不缓存,可以直接使用 loadfiledofile

  • dofile: 加载并立即运行脚本。
复制代码
dofile("example.lua") -- 执行 example.lua 中的代码

local chunk = ++loadfile("example.lua")++

chunk() -- 执行加载的代码块

相比之下,++require 更安全且高效,因为它具有模块缓存机制++。


8. requiremodule 的历史

Lua 5.1 曾经使用 module 函数来定义模块,但后来在 Lua 5.2 开始逐步弃用。现代 Lua 模块通常直接用表返回。


总结

  • require 是 ++Lua 中模块化编程的核心工具++。
  • 它++通过 package.pathpackage.cpath 实现模块搜索。++
  • 加载的模块++会被缓存到 package.loaded 中++,避免重复加载。
  • ++现代 Lua 使用 return 的表来组织模块内容。++

print(d)结果为nil

相关推荐
K3njuan1 小时前
《数据结构》学习系列——图(中)
数据结构·学习·深度优先
終不似少年遊*1 小时前
数学知识1
人工智能·学习·算法·机器学习·数学建模
.......xxx1 小时前
arm学习总结
linux·arm开发·学习
跳跳的向阳花1 小时前
Ajax学习笔记,第一节:语法基础
笔记·学习·ajax·axios
scc21402 小时前
kafka学习-02
分布式·学习·kafka
zmd-zk2 小时前
kafka生产者和消费者命令的使用
大数据·分布式·学习·kafka
hlsd#2 小时前
go web单体项目 学习总结
开发语言·学习·golang
awonw2 小时前
[运维][Nginx]Nginx学习(5/5)-Nginx高级
运维·学习·nginx
spencer_tseng3 小时前
World of Warcraft /script SetRaidTarget(“target“, n, ““) n=8,7,6,5,4,3,2,1,0
lua·wow