简体   繁体   English

python没有定义全局变量怎么解决?

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

I'm writing a script for stock / crpyto trading.我正在为股票/crpyto 交易编写脚本。 A very crude strategy is to buy when the RSI (a technical indicator in stock) reaches 50 and sell when the RSI falls below 50 for 3 consecutive days or below 40 in a single day.一个很粗略的策略是当RSI(股票的一个技术指标)达到50时买入,当RSI连续3天跌破50或单日跌破40时卖出。 My origin script is like this:我的原始脚本是这样的:

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

What this script is supposed to do is applying the aforementioned simple RSI strategy and return the 'buySellTable', which is a dictionary with the dates as keys and 'Buy' and 'Sell' as items.这个脚本应该做的是应用前面提到的简单 RSI 策略并返回“buySellTable”,这是一个以日期作为键、“Buy”和“Sell”作为项目的字典。 There is nothing wrong with this script.这个脚本没有错。 Since it's a crude strategy, it's necessary to get optimized.由于这是一个粗略的策略,因此有必要对其进行优化。 I want to split it into two parts - buying strategy and selling strategy so that I can do the optimization respectively.我想把它分成两部分——买入策略和卖出策略,这样我就可以分别进行优化。 Then I rewrote it into the following scripts:然后我将其重写为以下脚本:

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

In the 'simpleRSIstrategy_split' script, I want to treat buySellTable, hold, count and startIdx as global variables that can be used and edited by the scripts 'simpleBuyStrategy_split' and 'simpleSellStrategy_split'.在“simpleRSIstrategy_split”脚本中,我想将 buySellTable、hold、count 和 startIdx 视为可由脚本“simpleBuyStrategy_split”和“simpleSellStrategy_split”使用和编辑的全局变量。 But I got the error message when I run the script: name 'startIdx' is not defined.但是我在运行脚本时收到错误消息:未定义名称“startIdx”。 I check the output from the console and found that when it's excuting the line我从控制台检查 output 并发现当它执行该行时

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

in the script 'simpleBuyStrategy_split', it complains that startIdx is not defined.在脚本“simpleBuyStrategy_split”中,它抱怨未定义 startIdx。 But I already defined it as a global variable.但我已经将其定义为全局变量。 I don't know why this happens and how can I solve it?我不知道为什么会这样,我该如何解决?

Apparently you are unclear about the usage of the keyword global .显然你不清楚关键字global的用法。 The global keyword is used to access variables defined in a global scope inside a function. Consider this example, global关键字用于访问在 function 内的全局 scope 中定义的变量。考虑这个例子,

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

Here you get the output:在这里你得到 output:

0
>>>

because a local copy of the variable var is created when we call func() .因为当我们调用func()时会创建变量var的本地副本。 This copy of var , if modified, will not affect the global copy (ie the var = 0 defined outside func() in the global scope). var的这个副本,如果被修改,将不会影响全局副本(即在全局范围func()之外定义的var = 0 )。 Hence we get 0 as the output, since print (var) is in the global scope. Now consider this:因此我们得到0作为 output,因为print (var)在全局 scope 中。现在考虑这个:

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

Here you get the output:在这里你得到 output:

2
>>>

because when func() is called Python searches for var in the global scope, finds it, and changes its value to 2 .因为当func()时 Python 在全局 scope 中搜索var ,找到它,并将其值更改为2 Hence print (var) will show 2 because the global variable var was changed inside the function when we wrote var = 2 after global var .因此print (var)将显示2 ,因为当我们在global var之后写入var = 2时,全局变量var在 function 中发生了变化。

But if you do something like this:但是如果你这样做:

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

Here you get NameError while executing print (var) because var was never defiend in the global scope. When func() is called, Python searches for var in the global scope but doesn't find it and so just a local copy of the variable var is created.在这里,您在执行print (var)时得到NameError ,因为var从未在全局 scope 中定义。 func()时,Python 在全局 scope 中搜索var但没有找到它,因此只是变量的本地副本var创建。 So global var in this case is pretty much redundant because var was never defined in the global scope.因此,在这种情况下, global var变量几乎是多余的,因为var从未在全局 scope 中定义。

Simply put, the solution to your problem would be to put简而言之,解决您的问题的方法是

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

outside the function simpleRSIstrategy_split() .在 function simpleRSIstrategy_split()之外。 Your code should then work as expected.然后您的代码应按预期工作。

You got the scoping wrong.你弄错了范围。 Python is a lexically scoped language. Python 是一种词法范围的语言。

Without knowing, you are writing closures.不知不觉中,您正在编写闭包。 Meaning you use variables in simpleBuyStrategy_split and simpleSellStrategy_split which are not in the function argument list - but outside the function definition.这意味着您在simpleBuyStrategy_splitsimpleSellStrategy_split中使用的变量不在 function 参数列表中 - 但在 function 定义之外。

Basically基本上

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

You treat them as if they would have dynamic scoping.您将它们视为具有动态作用域。 (that they would change dependent where these two functions are called). (它们会根据调用这两个函数的位置而改变)。

Since python arguments are mostly call-by-reference (due to performance reasons) - so they work like C pointers, the arguments themselves are changed if they are changed in the function body - except when they are deep-copied explicitely in the function body.由于 python arguments 主要是按引用调用(由于性能原因) - 所以它们像 C 指针一样工作,如果它们在 function 主体中发生更改,则 arguments 本身也会更改 - 除非它们在 function 主体中被明确地深度复制主体34560845608 .

Therefore you could use them as if they are dynamically scoped, if you just pass them as function arguments.因此,如果您只是将它们作为 function arguments 传递,您就可以像在动态范围内一样使用它们。

Like this:像这样:

# 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

I see, somehow you realized that they have globally to refer, but then, the simpleRSI function split must also do it:我明白了,不知何故你意识到他们有全局引用,但是,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

And the other two functions then as you had them.然后其他两个功能就像您拥有的那样。 This might also work.这也可能有效。

But this is not so nice.但这不是很好。 Because one should avoid global variables.因为应该避免使用全局变量。

Solution: Encapsulate Using Classes解决方案:使用类进行封装

In Python, you would avoid global variables by using classes.在 Python 中,您将通过使用类来避免使用全局变量。 You could create a class where the global variables are class Attributes.您可以创建一个 class ,其中全局变量是 class 属性。 (prepended by: self. And these three functions are methods of the class. Since they have then to refer to these four variables using self. you don't need the 'global' delcarations in them. And the class itself is encapsulated. (前置: self.这三个函数是class的方法。因为它们必须使用self.你不需要它们中的“全局”声明。class本身被封装了。

Like this:像这样:

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

The good thing of this is, you can anytime from outside check the status of the attributes.这样做的好处是,您可以随时从外部检查属性的状态。 And thus also communicate with other parts of the program - while you don't have global variables - because the previously global variables are now encapsulated in the class - therefore packed into objects.因此也与程序的其他部分进行通信 - 虽然你没有全局变量 - 因为以前的全局变量现在封装在 class 中 - 因此打包到对象中。

So the idea is that for each indicator with new threshold settings and day settings you create a new instance for the simple RSI strategy.因此,我们的想法是,对于每个具有新阈值设置和日期设置的指标,您都可以为简单 RSI 策略创建一个新实例。

This class, you can reuse even for different RSI's (other than 'RSI7').这个 class,您甚至可以重复使用不同的 RSI(“RSI7”除外)。

Through encapsulation, the context for your buy and sell becomes clear - thus the method names or attribute names become simpler too - therefore everything more easily readable and your code is structured - every former global variables and functions are now encapsulated into the class - thus the code is clearly structured and the context of each of them clear - therefore you can be shorter with your names.通过封装,您买卖的上下文变得清晰 - 因此方法名称或属性名称也变得更简单 - 因此一切都更易于阅读并且您的代码结构化 - 每个以前的全局变量和函数现在都封装到 class - 因此代码结构清晰,每个代码的上下文都清晰 - 因此您可以使用更短的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM