和lua的效率对比测试_Unity游戏开发Lua更新运行时代码!

最近沉迷lua脚本热更,想说这个可以提高多少菜鸡的调试效率,找了网上好多文章,但是都不行,尝试了很久,并且自己测试和学习,写了一遍,勉强能热更了。下面记录一下热更Lua的过程。

一、用来卸载表格的加载

最简单粗暴的热更新就是将package.loaded[modelname]的值置为nil,强制重新加载:

function reload_module_obsolete(module_name)
package.loaded[module_name] = nil
require(module_name)
end

这样做虽然能完成热更,但问题是已经引用了该模块的地方不会得到更新, 因此我们需要将引用该模块的地方的值也做对应的更新。

function ReloadUtil.Reload_Module(module_name)
local old_module = _G[module_name]

package.loaded[module_name] = nil
require (module_name)

local new_module = _G[module_name]
for k, v in pairs(new_module) do
old_module[k] = v
end

package.loaded[module_name] = old_module
end

二、我认为逻辑最清晰的,但是可能是我不会用吧!

源链接:链接

--region 利用_ENV环境,在加载的时候把数据加载到_ENV下,然后再通过对比的方式修改_G底下的值,从而实现热更新,函数 -- 失败

function ReloadUtil.hotfix(chunk, check_name)
check_name = check_name or 'hotfix'
local env = {}
setmetatable(env, { __index = _G })
local f, err = load(chunk, check_name, 't', env)
assert(f,err)
local ok, err = pcall(f)
assert(ok,err)

local protection = {
setmetatable = true,
pairs = true,
ipairs = true,
next = true,
require = true,
_ENV = true,
}
--防止重复的table替换,造成死循环
local visited_sig = {}

function ReloadUtil.update_table(newTable, oldTable, name, deep)
--对某些关键函数不进行比对
if protection[newTable] or protection[oldTable] then return end
--如果原值与当前值内存一致,值一样不进行对比
if newTable == oldTable then return end
local signature = tostring(oldTable)..tostring(newTable)
if visited_sig[signature] then return end
visited_sig[signature] = true
--遍历对比值,如进行遍历env类似的步骤
for name, newValue in pairs(newTable) do
local old_value = oldTable[name]
if type(newValue) == type(old_value) then
if type(newValue) == 'function' then
ReloadUtil.update_func(newValue, old_value, name, deep..' '..name..' ')
oldTable[name] = newValue
elseif type(newValue) == 'table' then
ReloadUtil.update_table(newValue, old_value, name, deep..' '..name..' ')
end
else
oldTable[name] = newValue
end
end
--遍历table的元表,进行对比
local old_meta = debug.getmetatable(oldTable)
local new_meta = debug.getmetatable(newTable)
if type(old_meta) == 'table' and type(new_meta) == 'table' then
ReloadUtil.update_table(new_meta, old_meta, name..'s Meta', deep..' '..name..'s Meta'..' ' )
end
end

function ReloadUtil.update_func(newFunc, oldFunc, name, deep)
--取得原值所有的upvalue,保存起来
local old_upvalue_map = {}
for i = 1, math.huge do
local name, value = debug.getupvalue(oldFunc, i)
if not name then break end
old_upvalue_map[name] = value
end
--遍历所有新的upvalue,根据名字和原值对比,如果原值不存在则进行跳过,如果为其它值则进行遍历env类似的步骤
for i = 1, math.huge do
local name, value = debug.getupvalue(newFunc, i)
if not name then break end
local old_value = old_upvalue_map[name]
if old_value then
if type(old_value) ~= type(value) then
debug.setupvalue(newFunc, i, old_value)
elseif type(old_value) == 'function' then
ReloadUtil.update_func(value, old_value, name, deep..' '..name..' ')
elseif type(old_value) == 'table' then
ReloadUtil.update_table(value, old_value, name, deep..' '..name..' ')
debug.setupvalue(newFunc, i, old_value)
else
debug.setupvalue(newFunc, i, old_value)
end
end
end
end

--原理
--利用_ENV环境,在加载的时候把数据加载到_ENV下,然后再通过对比的方式修改_G底下的值,从而实现热更新,函数
for name,value in pairs(env) do
local g_value = _G[name]
if type(g_value) ~= type(value) then
_G[name] = value
elseif type(value) == 'function' then
ReloadUtil.update_func(value, g_value, name, 'G'..' ')
_G[name] = value
elseif type(value) == 'table' then
ReloadUtil.update_table(value, g_value, name, 'G'..' ')
end
end
return 0
end

function ReloadUtil.hotfix_file(debugName)
local newCode
local fp = io.open(debugName)
if fp then
newCode = fp:read('*all')
io.close(fp)
end
if not newCode then
return -1
end
return ReloadUtil.hotfix(newCode, debugName)
end

--endregion

23c90c0c8b308611241929b3c2b7fe64.png

图文无关

三、有点复杂,递归了debug.getregistry(),然后去替换旧的值

源链接:asqbtcupid.github.io/lu 有介绍原理,讲得还蛮细的,学习了蛮多,但是这个递归真的是复杂,我注释掉了一些递归,能满足基本的需求。

local ReloadUtil = {}

local tableInsert = table.insert
local tableRemove = table.remove
local tableConcat = table.concat
local ioPopen = io.popen
local ioInput = io.input
local ioRead = io.read
local stringMatch = string.match
local stringFind = string.find
local stringSub = string.sub
local stringGsub = string.gsub
local packageLoaded = package.loaded
local type = type
local getfenv = getfenv
local setfenv = setfenv
local loadstring = loadstring
local mathHuge = math.huge
local debugGetupvalue = debug.getupvalue
local debugSetupvalue = debug.setupvalue
local debugGetmetatable = debug.getmetatable
local debugSetfenv = debug.setfenv

function ReloadUtil.FailNotify(...)
printAError(...)
end

function ReloadUtil.DebugNofity(...)
print(...)
end

function ReloadUtil.ErrorHandle(e)
ReloadUtil.FailNotify("HotUpdate Error\n"..tostring(e))
ReloadUtil.ErrorHappen = true
end

function ReloadUtil.InitProtection()
ReloadUtil.Protection = {}
local Protection = ReloadUtil.Protection
Protection[setmetatable] = true
Protection[pairs] = true
Protection[ipairs] = true
Protection[next] = true
Protection[require] = true
Protection[ReloadUtil] = true
Protection[ReloadUtil.Meta] = true
Protection[math] = true
Protection[string] = true
Protection[table] = true
end

local function Normalize(path)
path = path:gsub("/","\\")

local pathLen = #path
if path:sub(pathLen, pathLen) == "\\" then
path = path:sub(1, pathLen - 1)
end

local parts = { }
for w in path:gmatch("[^\\]+") do
if w == ".." and #parts ~=0 then tableRemove(parts)
elseif w ~= "." then tableInsert(parts, w)
end
end
return tableConcat(parts, "\\")
end

-- 根据给的路径,找到路径下所有文件,HU.FileMap[FileName] = {SysPath = line, LuaPath = luapath}
function ReloadUtil.InitFileMap(RootPath)
local systemPathList = {}
local HotUpdateDic = ReloadUtil.HotUpdateDic
local OldCode = ReloadUtil.OldCode
RootPath = Normalize(RootPath)
--获取一个File对象其下的所有文件和目录的绝对路径: 的所有文件(/S/B),不包括文件夹(/A:A),ioPopen返回文件句柄file handle
--todo 这里有的问题,多次启动后会报bad file decorator ,检查是否是打开文件没有关闭导致
local file = ioPopen("dir /S/B /A:A \""..RootPath.."\"")

for SysPath in ioInput(file):lines() do
local FileName = stringMatch(SysPath,".*\\(.*)%.lua")
--todo meta 优化一下regex
local metaFile = stringMatch(SysPath,".*\\(.*)%.lua.meta")
if FileName ~= nil and metaFile == nil then
--todo !!! luaPath在保存的时候是按文件夹路径保存的比如:game.modules.XXX.lua,所以可能要自己搞对这个路径

local startIndex = stringFind(SysPath,"game")
local luaPath = stringSub(SysPath, startIndex, #SysPath -4)
luaPath = stringGsub(luaPath, "\\", ".")
HotUpdateDic[luaPath] = SysPath
tableInsert(systemPathList,SysPath)
-- 初始化旧代码
ioInput(SysPath)
OldCode[SysPath] = ioRead("*all")
ioInput():close()
end
end

file:close()

return systemPathList
end

function ReloadUtil.InitFakeTable()
local meta = {}
ReloadUtil.Meta = meta
local function FakeT() return setmetatable({}, meta) end
local function EmptyFunc() end
local function pairs() return EmptyFunc end
local function setmetatable(t, metaT)
ReloadUtil.MetaMap[t] = metaT
return t
end
local function getmetatable(t, metaT)
return setmetatable({}, t)
end

local function require(LuaPath)
if not ReloadUtil.RequireMap[LuaPath] then
local FakeTable = FakeT()
ReloadUtil.RequireMap[LuaPath] = FakeTable
end
return ReloadUtil.RequireMap[LuaPath]
end

function meta.__index(table, key)
if key == "setmetatable" then
return setmetatable
elseif key == "pairs" or key == "ipairs" then
return pairs
elseif key == "next" then
return EmptyFunc
elseif key == "require" then
return require
else
local FakeTable = FakeT()
rawset(table, key, FakeTable)
return FakeTable
end
end
function meta.__newindex(table, key, value) rawset(table, key, value) end
function meta.__call() return FakeT(), FakeT(), FakeT() end
function meta.__add() return meta.__call() end
function meta.__sub() return meta.__call() end
function meta.__mul() return meta.__call() end
function meta.__div() return meta.__call() end
function meta.__mod() return meta.__call() end
function meta.__pow() return meta.__call() end
function meta.__unm() return meta.__call() end
function meta.__concat() return meta.__call() end
function meta.__eq() return meta.__call() end
function meta.__lt() return meta.__call() end
function meta.__le() return meta.__call() end
function meta.__len() return meta.__call() end
return FakeT
end

function ReloadUtil.IsNewCode(SysPath)
ioInput(SysPath)
local newCode = ioRead("*all")

local oldCode = ReloadUtil.OldCode[SysPath]
if oldCode == newCode then
ioInput():close()
return false
end

ReloadUtil.DebugNofity(SysPath)
return true, newCode
end

function ReloadUtil.GetNewObject(newCode, LuaPath, SysPath)
--loadstring 一段lua代码以后,会经过语法解析返回一个函数,执行返回的函数时,字符串中的代码就被执行了。
local NewFunction = loadstring(newCode)
if not NewFunction then
ReloadUtil.FailNotify(SysPath.." has syntax error.")
collectgarbage("collect")
else
-- 把加载的字符串放置在空环境了,防止报错
setfenv(NewFunction, ReloadUtil.FakeENV)
local NewObject
ReloadUtil.ErrorHappen = false
--类似其它语言里的 try-catch, xpcall 类似 pcall xpcall接受两个参数:调用函数、错误处理函数
-- todo 父类没拿到
xpcall(function () NewObject = NewFunction() end, ReloadUtil.ErrorHandle)

if not ReloadUtil.ErrorHappen then
ReloadUtil.OldCode[SysPath] = newCode
return NewObject
else
collectgarbage("collect")
end
end
end

function ReloadUtil.ResetENV(object, name, From, Deepth)
local visited = {}
local function f(object, name)
if not object or visited[object] then return end
visited[object] = true
if type(object) == "function" then
ReloadUtil.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
xpcall(function () setfenv(object, ReloadUtil.ENV) end, ReloadUtil.FailNotify)
elseif type(object) == "table" then
ReloadUtil.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
for k, v in pairs(object) do
f(k, tostring(k).."__key", " HU.ResetENV ", Deepth.." " )
f(v, tostring(k), " HU.ResetENV ", Deepth.." ")
end
end
end
f(object, name)
end

-- 遍历_G这张全局表,替换HU.ChangedFuncList 有改动列表 的函数
function ReloadUtil.Travel_G()
local visited = {}
local ChangedFuncList = ReloadUtil.ChangedFuncList
visited[ReloadUtil] = true

local function f(table)
if (type(table) ~= "function" and type(table) ~= "table") or visited[table] or ReloadUtil.Protection[table] then return end

visited[table] = true

if type(table) == "function" then
for i = 1, mathHuge do
local name, value = debugGetupvalue(table, i)
if not name then break end

if type(value) == "function" then
for _, funcs in ipairs(ChangedFuncList) do
if value == funcs.OldObject then
debugSetupvalue(table, i, funcs.NewObject)
end
end
end
-- todo
--f(value)
end
elseif type(table) == "table" then
-- 不要漏掉元表和upvalue的表,元表的获取用debug.getmetatable,
-- todo 这样对于有metatable这个key的元表,也能正确获取。
--f(debugGetmetatable(table))

local changeIndexList = {}
for key, value in pairs(table) do
-- todo 还有注意table的key也可以是函数。
--f(key)
f(value)

if type(value) == "function" then
for _, funcs in ipairs(ChangedFuncList) do
if value == funcs.OldObject then
table[key] = funcs.NewObject
end
end
end

-- 找出改动的index
if type(key) == "function" then
for index, funcs in ipairs(ChangedFuncList) do
if key == funcs.OldObject then
changeIndexList[#changeIndexList + 1] = index
end
end
end
end

-- 修改改动的值
for _, index in ipairs(changeIndexList) do
local funcs = ChangedFuncList[index]
table[funcs.NewObject] = table[funcs.OldObject]
table[funcs.OldObject] = nil
end
end
end

--遍历_G这张全局表,_G在registryTable里
--f(_G)
--如果有宿主语言,那么还要遍历一下注册表,用debug.getregistry()获得。
local registryTable = debug.getregistry()
f(registryTable)
end

function ReloadUtil.UpdateTable(OldTable, NewTable, Name, From, Deepth)
if ReloadUtil.Protection[OldTable] or ReloadUtil.Protection[NewTable] then return end

if OldTable == NewTable then return end

local signature = tostring(OldTable)..tostring(NewTable)

if ReloadUtil.VisitedSig[signature] then return end

ReloadUtil.VisitedSig[signature] = true
ReloadUtil.DebugNofity(Deepth.."HU.UpdateTable "..Name.." from:"..From)

for ElementName, newValue in pairs(NewTable) do
local OldElement = OldTable[ElementName]
if type(newValue) == type(OldElement) then
if type(newValue) == "function" then
ReloadUtil.UpdateOneFunction(OldElement, newValue, ElementName, OldTable, "HU.UpdateTable", Deepth.." ")
elseif type(newValue) == "table" then
ReloadUtil.UpdateTable(OldElement, newValue, ElementName, "HU.UpdateTable", Deepth.." ")
end
elseif OldElement == nil and type(newValue) == "function" then
-- 新增的函数,添加到旧环境里
if pcall(setfenv, newValue, ReloadUtil.ENV) then
OldTable[ElementName] = newValue
end
end
end

-- todo 更新metatable
--local OldMeta = debug.getmetatable(OldTable)
--local NewMeta = ReloadUtil.MetaMap[NewTable]
--if type(OldMeta) == "table" and type(NewMeta) == "table" then
-- ReloadUtil.UpdateTable(OldMeta, NewMeta, Name.."'s Meta", "HU.UpdateTable", Deepth.." ")
--end
end

-- Upvalue 是指那些函数外被引用到的local变量
function ReloadUtil.UpdateUpvalue(OldFunction, NewFunction, Name, From, Deepth)
ReloadUtil.DebugNofity(Deepth.."HU.UpdateUpvalue", Name, " from:"..From)
local OldUpvalueMap = {}
local OldExistName = {}
-- 记录旧的upvalue表
for i = 1, mathHuge do
local name, value = debugGetupvalue(OldFunction, i)
if not name then break end
OldUpvalueMap[name] = value
OldExistName[name] = true
end

-- 新的upvalue表进行替换
for i = 1, mathHuge do
local name, value = debugGetupvalue(NewFunction, i)
if not name then break end
if OldExistName[name] then
local OldValue = OldUpvalueMap[name]
if type(OldValue) ~= type(value) then
-- 新的upvalue类型不一致时,用旧的upvalue
debugSetupvalue(NewFunction, i, OldValue)
elseif type(OldValue) == "function" then
-- 替换单个函数
ReloadUtil.UpdateOneFunction(OldValue, value, name, nil, "HU.UpdateUpvalue", Deepth.." ")
elseif type(OldValue) == "table" then
-- 对table里面的函数继续递归替换
ReloadUtil.UpdateTable(OldValue, value, name, "HU.UpdateUpvalue", Deepth.." ")
debugSetupvalue(NewFunction, i, OldValue)
else
-- 其他类型数据有改变,也要用旧的
debugSetupvalue(NewFunction, i, OldValue)
end
else
-- 对新添加的upvalue设置正确的环境表
ReloadUtil.ResetENV(value, name, "HU.UpdateUpvalue", Deepth.." ")
end
end
end

function ReloadUtil.UpdateOneFunction(OldObject, NewObject, FuncName, OldTable, From, Deepth)
if ReloadUtil.Protection[OldObject] or ReloadUtil.Protection[NewObject] then return end

if OldObject == NewObject then return end

local signature = tostring(OldObject)..tostring(NewObject)

if ReloadUtil.VisitedSig[signature] then return end
ReloadUtil.DebugNofity(Deepth.."HU.UpdateOneFunction "..FuncName.." from:"..From)
ReloadUtil.VisitedSig[signature] = true
--最后注意把热更新的函数的环境表再改回旧函数的环境表即可,方法是setfenv(newfunction, getfenv(oldfunction))。
if pcall(debugSetfenv, NewObject, getfenv(OldObject)) then
ReloadUtil.UpdateUpvalue(OldObject, NewObject, FuncName, "HU.UpdateOneFunction", Deepth.." ")
ReloadUtil.ChangedFuncList[#ReloadUtil.ChangedFuncList + 1] = {OldObject = OldObject,NewObject = NewObject,FuncName = FuncName,OldTable = OldTable}
end
end

function ReloadUtil.ReplaceOld(OldObject, NewObject, LuaPath, From)
if type(OldObject) == type(NewObject) then
if type(NewObject) == "table" then
ReloadUtil.UpdateTable(OldObject, NewObject, LuaPath, From, "")
elseif type(NewObject) == "function" then
ReloadUtil.UpdateOneFunction(OldObject, NewObject, LuaPath, nil, From, "")
end
end
end

function ReloadUtil.HotUpdateCode(LuaPath, SysPath)
local OldObject = packageLoaded[LuaPath]
if not OldObject then
-- 没加载的就不热更??
return
end

local isNew,newCode = ReloadUtil.IsNewCode(SysPath)
if not isNew then
return
end

local newObject = ReloadUtil.GetNewObject(newCode,SysPath,LuaPath)
-- 更新旧代码
ReloadUtil.ReplaceOld(OldObject, newObject, LuaPath, "Main")

--原理
--利用_ENV环境,在加载的时候把数据加载到_ENV下,然后再通过对比的方式修改_G底下的值,从而实现热更新,函数
--setmetatable(ReloadUtil.FakeENV, nil)
--todo ??
--ReloadUtil.UpdateTable(ReloadUtil.ENV, ReloadUtil.FakeENV, " ENV ", "Main", "")

-- 替换完,上一次的代码就是旧代码
ReloadUtil.OldCode[SysPath] = newCode
end


-- 外部调用(先Init需要的路径,然后Update是热更时候调用的)

---@param RootPath 需要被更新的文件夹路径
---@param UpdateListFile 需要被更新的文件列表,不传为整个文件夹,会卡
function ReloadUtil.Init(RootPath, ENV)
ReloadUtil.HotUpdateDic = {}
ReloadUtil.FileMap = {}
ReloadUtil.OldCode = {}
ReloadUtil.ChangedFuncList = {}
ReloadUtil.VisitedSig = {}
ReloadUtil.FakeENV = ReloadUtil.InitFakeTable()()
--当我们加载lua模块的时候,这时候这个模块信息并不像初始化全局代码一样,就算提前设置了package.loaded["AA"] = nil,
--也不会出现在env中同时也不会调用_G的__newindex函数,也就是说env["AA"]为空,故这种写法无法进行热更新,所以通常模块的写法改成如下
--定义模块AA
local AA = {}
--相当于package.seeall
setmetatable(AA, {__index = _G})
--环境隔离
local _ENV = AA
ReloadUtil.NewENV = _ENV
ReloadUtil.ENV = ENV or _G
ReloadUtil.InitProtection()
ReloadUtil.ALL = false
return ReloadUtil.InitFileMap(RootPath)
end

function ReloadUtil.Update()
ReloadUtil.VisitedSig = {}
ReloadUtil.ChangedFuncList = {}
for LuaPath, SysPath in pairs(ReloadUtil.HotUpdateDic) do
ReloadUtil.HotUpdateCode(LuaPath, SysPath)
end

if #ReloadUtil.ChangedFuncList > 0 then
ReloadUtil.Travel_G()
end
collectgarbage("collect")
end

我跟老大炫耀的时候,老大说,那你懂其中的原理吗,一下问懵我了,老大说,你要学习到其中的原理才能进步啊,不然就只是个会用工具的人。好有道理,搞得我羞愧难当,赶紧好好学习其中原理。

Upvalue

Upvalue 是指那些函数外被引用到的local变量,比如:
local a = 1
function foo()
print(a)
end
那么a就是这个foo的upvalue。

getupvalue (f, up)

此函数返回函数 f 的第 up 个上值的名字和值。如果该函数没有那个上值,返回 nil 。
以 '(' (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。

setupvalue (f, up, value):

这个函数将 value 设为函数 f 的第 up 个上值。如果函数没有那个上值,返回 nil 否则,返回该上值的名字。

_G和debug.getregistry这2张表

学习的时候,我一直以为只有把_G这张全局表的旧值替换掉就好了,然后真正实施的时候,还是会有种种问题,实在是很糟糕,看这段代码的时候一直不是很理解,看了debug.getregistry的定义:debug.getregistry():返回注册表表,这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值。
还是半桶水,但是我有注意到_G这种表其实在debug.getregistry返回的这张注册表里有,所以最后就递归这张表其替换里面的旧值就好了。

getfenv(object):返回对象的环境变量。
setfenv(function,_ENV):设置一段代码的运行环境

io.popen("dir /S/B /A:A \""..RootPath.."\""):获取一个File对象其下的所有文件和目录的绝对路径: 的所有文件(/S/B),不包括文件夹(/A:A),io.popen返回文件句柄file handle

对了,InitFileMap这个函数有个要注意的,luaPath在保存的时候是按文件夹路径保存的比如:game.modules.XXX.lua,所以可能要自己搞对这个路径;

最后总结一下流程:

1. 初始化需要热更的文件路径,用一张哈希表存下来;
2. 然后遍历这些路径,读取所有的代码,判断是否是新代码,新的话记录到ChangedFuncList列表里面;3. 新代码的话,就把加载的字符串放置在空环境了,防止报错setfenv(NewFunction, ReloadUtil.FakeENV);4. 代替旧代码,拿着ChangedFuncList列表到注册表(debug.getregistry())里面去找旧值,递归注册表的旧值替换成新的值。

来源知乎专栏:Unity手游开发叨叨

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nodejs cluster ip hash_redis集群架构了解一下?一致性hash了解吗?

在前几年,redis 如果要搞几个节点,每个节点存储一部分的数据,得借助一些中间件来实现,比如说有 codis,或者 twemproxy,都有。有一些 redis 中间件,你读写 redis 中间件,redis 中间件…

硬盘主分区和拓展分区

主分区,也称为主磁盘分区,和扩展分区、逻辑分区一样,是一种分区类型。主分区中不能再划分其他类型的分区,因此每个主分区都相当于一个逻辑磁盘(在这一点上主分区和逻辑分区很相似,但主分区是直接在硬盘上划分的,逻辑分区则必须建立于扩展分区中)。 1. 一个硬盘可以有1到3个主分…

python如何比较大小_python列表如何比较大小

python列表如何比较大小 发布时间:2020-09-22 13:58:58 来源:亿速云 阅读:59 作者:小新 这篇文章给大家分享的是有关python列表如何比较大小的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。 Python中可以使用cmp()函数比较两个列表的大小。 c…

python 连接oracle_常用的Python库,给大家分享一下!

Tkinter———— Python默认的图形界面接口。Tkinter是一个和Tk接口的Python模块,Tkinter库提供了对Tk API的接口,它属于Tcl/Tk的GUI工具组。Tcl/Tk是由John Ousterhout发展的书写和图形设备。Tcl(工具命令语言)是个宏语言,用于简化shell下复…

js 获取某年的某天是第几周

/**2 * 判断年份是否为润年3 *4 * param {Number} year5 */6 function isLeapYear(year) {7 return (year % 400 0) || (year % 4 0 && year % 100 ! 0);8 }9 /**10 * 获取某一年份的某一月份的天数11 *12 * param {Number} year13 * param {Number} month14 *…

python 前端学习_python学习之路7 前端学习3

1.页面布局PosTion :fixedTitle.left{float:left;}.right{float:right;}.head{height:58px;background-color:#3c3c3c;}.head .fontsquarl{width:120px;height:58px;background-color:#F22E00;line-height:58px;}.head .logosuqarl{width:120px;height:58px;background-color:#…

2.5d generator 2.0_ps插件【2.5D插件】

近一两年来创意插画在视觉上的使用越来越多,设计师们都愈加倾向于使用漂亮的插画和产品结合让自己的设计脱颖而出。在最近这段时间新涌现的设计作品当中,强烈的视觉冲击力和产品结合的展现是它们共通的特点。那么2.5D插画也是作为近一两年的发展趋势&…

(转)女生应该找一个玩ACM的男生

1、强烈的事业心 将来,他也一定会有自己热爱的事业。而且,男人最性感的时刻之一,就是他专心致志做事的时候。所以,找一个机会在他全神贯注玩ACM的时候,从侧面好好观察他,你就会发现我说的话没错。 2、永不放…

人工神经网络_制作属于自己的人工神经网络

在本文中,我已经实现了具有Dropout和L2正则化的人工神经网络的完全向量化代码。在本文中,我实现了一个在多个数据集上测试的人工神经网络的完全向量化python代码。此外,并对Dropout和L2正则化技术进行了实现和详细说明。强烈建议通过人工神经…

decorators 参数_Python Decorators(二):Decorator参数

Python Decorators II: Decorator ArgumentsOctober 19, 2008(本文是(Python3之模式和用法)一书的章节节选第二部分,点击阅读第一部分)回顾:不含参数的decorators在前文中,我介绍了如何使用不含参数的decorators,并使用类来实现。…

[leedcode 52] N-Queens II

Follow up for N-Queens problem. Now, instead outputting board configurations, return the total number of distinct solutions. public class Solution {//本题类似于N-Queens,这个更简单一些,只需要求出解法的个数即可,因此没有了prin…

python文本风格_以写代学:python 良好的代码风格实例解析

将用户输入的数据存入到一个文件当中去,这个文件必须是原来不存在的 (1)起始行 (2)模块文档字符串 (3)导入模块 (4)全局变量声明 (5)类定义 &…

三角形和矩形傅里叶变换_信号与系统:第三章傅立叶变换2.ppt

第三节 连续时间周期信号的频谱分析 一)周期矩形脉冲的频谱 三、 周期信号的有效频帶宽度(简称带宽) 四、 周期信号的功率谱 第四节、 连续时间非周期信号的频谱 一、 从傅立叶级数到傅里叶变换 三、一些典型信号的 频谱函数F(jw) (即傅里叶变换) (2)单边指数信号的傅里叶变换 …

Sock基础

z1 客户端 //客户端 通信套接字 //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接 Socket sokMsg new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.获取要连接的服务端 节点 //2.1获取网络节点对象 IPAddress add…

python转r字符_python r不转义变量

普通字符串常量 含有转义字符,会按照转义字符的意义输出,如下:text"1 E:/Code/PycharmProjects/QtDemo/ToolsList\__pycache__\start.cpython-36.pyc \r\n" print(text) 输出如下:1 E:/Code/PycharmProjects/QtDemo/Too…

uinty粒子系统子物体变大_Unity2018粒子系统全息讲解,坑深慎入(3)

马上注册,加入CGJOY,让你轻松玩转CGJOY。您需要 登录 才可以下载或查看,没有帐号?立即注册x200357v0p9jufzelwj0uuj.jpg (60.94 KB, 下载次数: 16)2018-6-11 20:22 上传声明!声明!声明!这不会让…

第16/24周 SQL Server 2014中的基数计算

大家好,欢迎回到性能调优培训。上个星期我们讨论在SQL Server里基数计算过程里的一些问题。今天我们继续详细谈下,SQL Server 2014里引入的新基数计算。 新基数计算 SQL Server 2014里一个增强是新的基数计算。上个星期你已经学到老基数计算有些限制&…

python爬虫知乎图片_Python爬虫入门教程 25-100 知乎文章图片爬取器之一

1. 知乎文章图片爬取器之一写在前面 今天开始尝试爬取一下知乎,看一下这个网站都有什么好玩的内容可以爬取到,可能断断续续会写几篇文章,今天首先爬取最简单的,单一文章的所有回答,爬取这个没有什么难度。 找到我们要爬…

mysql主从复制不同步案例_Mysql主从不同步问题处理案例

在使用Mysql的主从复制架构中,有两个比较头疼的问题:1、主从数据不同步后如何处理2、主从同步延迟问题如何解决本文将根据实际案例来分析下问题1,至于问题2多数文档介绍的办法是启用多线程复制来解决,言归正传,这里的问…

九度oj题目1518:反转链表

题目1518:反转链表 时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:2567 解决:948 题目描述:输入一个链表,反转链表后,输出链表的所有元素。(hint : 请务必使用链表) 输…