1. 程式人生 > 程式設計 >Python中實現輸入超時及如何通過變數獲取變數名

Python中實現輸入超時及如何通過變數獲取變數名

背景介紹

開發中遇到了一個需求:程式執行到某處時需要使用者確認,但不能一直傻等,後面的程式不能被一直阻塞,需要有個超時限制,也就是這個程式如果在一段時間後還沒有得到使用者輸入就執行預設操作.

解決思路 – 多執行緒法

我就想到了用多執行緒的方式,開啟一個子執行緒用stdin(比如python的input函式)獲取使用者輸入,主執行緒裡設定執行緒啟動和超時.

建立執行緒

Python中使用多執行緒很方便,threading.Threaded(函式,引數表)然後thread.start就好了. 只是有一點需要注意,上面引數表必須是個元組,也就是每個元素後面必須跟個逗號.

import threading
def anyFunction(aaa):
 return str(aaa) #某種處理結果,比如字串
def manualInput(xxx):
 data = input("請輸入%s: " % xxx)
 pass # 各種處理(比如資料轉換什麼的)
 
exampleThread = threading.Thread(target = manualInput,args = ( anyFunction("汪汪汪"),),name = "喵喵喵")
exampleThread.start()

通過函式修改某個指定的(通過名字即字串)變數的值
但這又出來一個問題,如果不能使用全域性變數,該如何在另一個函式裡修改其引數對應的內容呢? 這裡的重點歸結起來是"函式如何修改自身引數的內容".

於是我想到了一個騷透了的方法——改變數字典…… 因為python的變數是基於標籤的. python中的變數大致可以理解成給內容貼上標籤(每個標籤對應一個變數名,多個標籤可能會引用同一個內容,沒被引用的內容就會被python釋放),每個標籤都會有一個id(同時,一個記憶體資料只要被引用那麼自身也有個id). 示例:

print(id("喵喵喵"),"~~",id("喵喵喵"),"~~")
[Out]: 
1392371317520 ~~ 1392371317520 ~~ 1392371317520 ~~
print(id("喵喵喵")); print(id("喵喵喵")); print(id("喵喵喵"))
[Out]: 
1392371318576
1392371318000
1392371318288

python維護這些標籤和內容的對應關係可以通過字典的方式來讀取和修改,改globals()[待改的變數的原名]的值就能通過指定變數名來修改變量了.

通過globals的字典修改變數

通過變數來獲取變數的名字(字串)
上面通過globals()[待改的變數的原名] = 新的內容的方式實現了修改變數的內容,可是,待改的變數的原名是個字串,怎麼通過變數得到這個變數的名字呢?

一個思路是字典法.

把當前執行環境中的所有變數複製一份(淺拷貝和深拷貝效果都一樣,因為深淺拷貝前後都是相同的標籤),然後新建一個"標籤id-變數名"的對照表字典,利用字典賦值的特性,遍歷複製來的全域性變數,把id(變數值)作為key而變數名作為value,即標籤id-變數名字典[id(變數值)] = 變數名.

test = "some values"   
變數A = "汪汪汪" 
當前所有變數 = globals().copy()
print(當前所有變數)

[Out]:
{'__name__': '__main__',...,'test': 'some values','變數A': '汪汪汪'}

內容_變數名字串對照表 = {} 
for 變數名,變數值 in 當前所有變數.items():
 內容_變數名字串對照表[id(變數值)] = 變數名
print(內容_變數名字串對照表)

[Out]:
{2437914516272: '__name__',2437948462576: 'test',2437948432816:'變數A'}

這樣一來就建立一個內容-變數名字串的對照表,又因為id(變數A) 和 id(變數A的值)是相等,利用這個特性就能通過變數來取變數值了.

變數A的值 = 變數A
print(id(變數A的值)
[Out]:
2437948432816
內容_變數名字串對照表[id(變數A的值)]
[Out]:
'變數A'

通過函式修改變數

上面這一堆頭髮就是為了動態、通用地修改變數,封裝成函式就能在任何地方呼叫和修改了.

def 一個實現變數修改函式(要改的變數,提示語):
 當前所有變數 = globals().copy()
 變數id表 = {}
 for 變數名,變數值 in 當前所有變數.items():
  變數id表[id(變數值)] = 變數名
 待改的變數的原名 = 變數id表[id(要改的變數)]
 新的內容 = str(input(提示語))
 if len(新的內容) > 0 :
  globals()[待改的變數的原名] = 新的內容
 return 待改的變數的原名
tmp = "汪汪汪"

一個實現變數修改函式(tmp,"請輸入新值: ")

[Out]:
請輸入新值: 喵喵喵
 'tmp'
print(tmp)
[Out]:
喵喵喵

總結(demo)[不想看中間過程的話可以直接看這]

import time,threading
# 這裡的demo是為了通用化. 因為在一個執行緒中再巢狀另個執行緒的話,巢狀的執行緒獲取不到所有變數
class ThreadWithReturn(threading.Thread):
 def __init__( self,target = None,args = () ):
  super(ThreadWithReturn,self).__init__()
  self.func = target
  self.args = args
 def run(self):
  self.result = self.func(*self.args)
 def getResult(self):
  try:
   return self.result
  except Exception as errInfo:
   print("遇到錯誤: ",errInfo)
   return None
def 一個實現變數修改函式(要改的變數,變數值 in 當前所有變數.items():
  變數id表[id(變數值)] = 變數名
 try:
  待改的變數的原名 = 變數id表[id(要改的變數)]
 except KeyError:
  print("***debug: 在不同的執行緒中執行,獲取不到出入變數的名字")
  待改的變數的原名 = None
 新的內容 = str(input(提示語))
 if len(新的內容) > 0 :
  if 待改的變數的原名 != None:
   globals()[待改的變數的原名] = 新的內容
 else:
  新的內容 = None
 return [待改的變數的原名,新的內容]
def Gexit():
 exitConfirm = "u"
 waitForConirm = ThreadWithReturn( target = 一個實現變數修改函式,args = (exitConfirm,"收到了退出訊號,預設30秒後退出,是否現在退出呢? (Y/n) 請輸入: ",) )
 waitForConirm.start()
 waitForConirm.join(30)
 try:
  exitConfirm = waitForConirm.getResult()[1]
  print("***debug,got:",exitConfirm)
 except Exception as errInfo:
  print("***debug:",errInfo)
  exitConfirm = "u"
 if exitConfirm == "u":
  print("等待超時,開始退出流程...")
  exitConfirm = "Ytt"
 if exitConfirm == "Ytt" or exitConfirm == "Y":
  if exitConfirm == "Y":
   print("確認退出,開始退出流程...")
   pass # 這裡放程式退出邏輯
 if exitConfirm == "n":
  print("取消退出,繼續執行...")
  pass # 這裡放繼續執行的邏輯
 return 0
Thread_waitForExit = threading.Thread(target = Gexit,args = ())
Thread_waitForExit.start()
Thread_waitForExit.join(45)

總結

以上所述是小編給大家介紹的Python中實現輸入超時及如何通過變數獲取變數的名字,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對我們網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!