簡體   English   中英

python沒有定義全局變量怎么解決?

[英]How to solve the problem that the global variable is not defined in python?

我正在為股票/crpyto 交易編寫腳本。 一個很粗略的策略是當RSI(股票的一個技術指標)達到50時買入,當RSI連續3天跌破50或單日跌破40時賣出。 我的原始腳本是這樣的:

def simpleRSIstrategy(indicator, threshold1 = 50, threshold2 = 40, days = 3):
    
    buySellTable = dict()
    
    # simple RSI strategy
      
    hold = False
    count = 0  
    
    for i in range(len(indicator)):
        # buying strategy
        if indicator['RSI7'][i] > threshold1 and not hold:
            date = indicator.index[i]
            buySellTable[date] = 'Buy'
            hold = True  
            
        # selling strategy    
        if indicator['RSI7'][i] < threshold1 and hold:
            count += 1
            if count == days or indicator['RSI7'][i] < threshold2:
                date = indicator.index[i]
                buySellTable[date] = 'Sell'
                hold = False
                count = 0       
        if indicator['RSI7'][i] > threshold1 and hold:
            count = 0
    return buySellTable

這個腳本應該做的是應用前面提到的簡單 RSI 策略並返回“buySellTable”,這是一個以日期作為鍵、“Buy”和“Sell”作為項目的字典。 這個腳本沒有錯。 由於這是一個粗略的策略,因此有必要對其進行優化。 我想把它分成兩部分——買入策略和賣出策略,這樣我就可以分別進行優化。 然后我將其重寫為以下腳本:

def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3):
    
    
    buySellTable = dict()
    hold = False
    count = 0 
    startIdx = 0
   
    while True:
       
        simpleBuyStrategy_split(indicator, threshold1)
     
        simpleSellStrategy_split(indicator, threshold1, threshold2, days)
        
        if startIdx == len(indicator)-1:
            break
        
    return buySellTable
def simpleBuyStrategy_split(indicator, threshold1):
    global startIdx, hold, buySellTable
    
    for i in range(startIdx, len(indicator)):
            if indicator['RSI7'][i] > threshold1 and not hold:
                date = indicator.index[i]
                buySellTable[date] = 'Buy'
                hold = True  
                startIdx = i+1
                break

def simpleSellStrategy_split(indicator, threshold1, threshold2,  days):
    global startIdx, count, hold, buySellTable
    
    for i in range(startIdx, len(indicator)):
        if indicator['RSI7'][i] < threshold1 and hold:
            count += 1
            if count == days or indicator['RSI7'][i] < threshold2:
                date = indicator.index[i]
                buySellTable[date] = 'Sell'
                hold = False
                count = 0  
                startIdx = i+1
                break
        if indicator['RSI7'][i] > threshold1 and hold:
            count = 0

在“simpleRSIstrategy_split”腳本中,我想將 buySellTable、hold、count 和 startIdx 視為可由腳本“simpleBuyStrategy_split”和“simpleSellStrategy_split”使用和編輯的全局變量。 但是我在運行腳本時收到錯誤消息:未定義名稱“startIdx”。 我從控制台檢查 output 並發現當它執行該行時

for i in range(startIdx, len(indicator)):

在腳本“simpleBuyStrategy_split”中,它抱怨未定義 startIdx。 但我已經將其定義為全局變量。 我不知道為什么會這樣,我該如何解決?

顯然你不清楚關鍵字global的用法。 global關鍵字用於訪問在 function 內的全局 scope 中定義的變量。考慮這個例子,

var = 0
def func():
    var = 2
func()
print (var)

在這里你得到 output:

0
>>>

因為當我們調用func()時會創建變量var的本地副本。 var的這個副本,如果被修改,將不會影響全局副本(即在全局范圍func()之外定義的var = 0 )。 因此我們得到0作為 output,因為print (var)在全局 scope 中。現在考慮這個:

var = 0
def func():
    global var
    var = 2
func()
print (var)

在這里你得到 output:

2
>>>

因為當func()時 Python 在全局 scope 中搜索var ,找到它,並將其值更改為2 因此print (var)將顯示2 ,因為當我們在global var之后寫入var = 2時,全局變量var在 function 中發生了變化。

但是如果你這樣做:

def func():
    global var
    var = 2
func()
print (var)

在這里,您在執行print (var)時得到NameError ,因為var從未在全局 scope 中定義。 func()時,Python 在全局 scope 中搜索var但沒有找到它,因此只是變量的本地副本var創建。 因此,在這種情況下, global var變量幾乎是多余的,因為var從未在全局 scope 中定義。

簡而言之,解決您的問題的方法是

buySellTable = dict()
hold = False
count = 0 
startIdx = 0

在 function simpleRSIstrategy_split()之外。 然后您的代碼應按預期工作。

你弄錯了范圍。 Python 是一種詞法范圍的語言。

不知不覺中,您正在編寫閉包。 這意味着您在simpleBuyStrategy_splitsimpleSellStrategy_split中使用的變量不在 function 參數列表中 - 但在 function 定義之外。

基本上

    buySellTable = dict()
    hold = False
    count = 0 
    startIdx = 0

您將它們視為具有動態作用域。 (它們會根據調用這兩個函數的位置而改變)。

由於 python arguments 主要是按引用調用(由於性能原因) - 所以它們像 C 指針一樣工作,如果它們在 function 主體中發生更改,則 arguments 本身也會更改 - 除非它們在 function 主體中被明確地深度復制主體34560845608 .

因此,如果您只是將它們作為 function arguments 傳遞,您就可以像在動態范圍內一樣使用它們。

像這樣:

# first define them globally

buySellTable = dict()
hold = False
count = 0 
startIdx = 0

# then in each function definition, pass them as function arguments

def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):

    while True:
       
        simpleBuyStrategy_split(indicator, threshold1, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx)
     
        simpleSellStrategy_split(indicator, threshold1, threshold2, days, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx)
        
        if startIdx == len(indicator)-1:
            break
        
    return buySellTable

def simpleBuyStrategy_split(indicator, threshold1, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):
    
    for i in range(startIdx, len(indicator)):
            if indicator['RSI7'][i] > threshold1 and not hold:
                date = indicator.index[i]
                buySellTable[date] = 'Buy'
                hold = True  
                startIdx = i+1
                break

def simpleSellStrategy_split(indicator, threshold1, threshold2,  days, buySellTable=buySellTable, hold=hold, count=count, startIdx=startIdx):

    for i in range(startIdx, len(indicator)):
        if indicator['RSI7'][i] < threshold1 and hold:
            count += 1
            if count == days or indicator['RSI7'][i] < threshold2:
                date = indicator.index[i]
                buySellTable[date] = 'Sell'
                hold = False
                count = 0  
                startIdx = i+1
                break
        if indicator['RSI7'][i] > threshold1 and hold:
            count = 0

我明白了,不知何故你意識到他們有全局引用,但是,simpleRSI function split 也必須這樣做:


buySellTable = dict()
hold = False
count = 0 
startIdx = 0
   
def simpleRSIstrategy_split(indicator, threshold1 = 50, threshold2 = 40, days = 3):
    global buySellTable, hold, count, startIdx
    
    while True:
       
        simpleBuyStrategy_split(indicator, threshold1)
     
        simpleSellStrategy_split(indicator, threshold1, threshold2, days)
        
        if startIdx == len(indicator)-1:
            break
        
    return buySellTable

然后其他兩個功能就像您擁有的那樣。 這也可能有效。

但這不是很好。 因為應該避免使用全局變量。

解決方案:使用類進行封裝

在 Python 中,您將通過使用類來避免使用全局變量。 您可以創建一個 class ,其中全局變量是 class 屬性。 (前置: self.這三個函數是class的方法。因為它們必須使用self.你不需要它們中的“全局”聲明。class本身被封裝了。

像這樣:

class SimpleRSITrader:
    def __init__(self, indicator, threshold1 = 50, threshold2 = 40, days = 3, rsi='RSI7'):
        self.buy_sell_table = {}
        self.hold = False
        self.count = 0
        self.start_idx = 0
        self.indicator = indicator
        self.thresh1 = threshold1
        self.thrseh2 = threshold2
        self.days = days
        self.rsi = rsi
    
    def run(self):
        while True:
            self.buy()
            self.sell()     
            if self.startIdx == len(self.indicator)-1:
                break
        return self.buy_sell_table
    
    def buy(self):
        for i in range(self.start_idx, len(self.indicator)):
            if self.indicator[self.rsi][i] > self.thresh1 and not self.hold:
                date = self.indicator.index[i]
                self.buy_sell_table[date] = 'Buy'
                self.hold = True
                self.start_idx = i + 1
                break
                
    def sell(self):
        for i in range(self.start_idx, len(self.indicator)):
            if self.hold:
                if self.indictaor[self.rsi] < self.thresh1:
                    self.count += 1
                    if count == self.days or self.indictoar[self.rsi][i] < self.thresh2:
                        date = self.indicator.index[i]    
                        self.buy_sell_table[date] = 'Sell'
                        self.hold = False
                        self.count = 0
                        self.start_idx = i + 1
                        break
                if self.indicator[self.rsi][i] > self.thresh1:
                    self.count = 0

這樣做的好處是,您可以隨時從外部檢查屬性的狀態。 因此也與程序的其他部分進行通信 - 雖然你沒有全局變量 - 因為以前的全局變量現在封裝在 class 中 - 因此打包到對象中。

因此,我們的想法是,對於每個具有新閾值設置和日期設置的指標,您都可以為簡單 RSI 策略創建一個新實例。

這個 class,您甚至可以重復使用不同的 RSI(“RSI7”除外)。

通過封裝,您買賣的上下文變得清晰 - 因此方法名稱或屬性名稱也變得更簡單 - 因此一切都更易於閱讀並且您的代碼結構化 - 每個以前的全局變量和函數現在都封裝到 class - 因此代碼結構清晰,每個代碼的上下文都清晰 - 因此您可以使用更短的名稱。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM