有没有一种方法可以解决由于范围引起的名称错误?

时间:2019-07-29 08:33:47

标签: python

我有一个创建播放器对象的函数,但是当引用该对象时,会出现NameError。我认为这是由于本地范围而发生的,但全球应该解决此问题...

我刚开始使用OOP,此代码在python shell中有效,但在脚本模式下不起作用。

endl = lambda a: print("\n"*a)

class Score:
    _tie = 0
    def __init__(self):
        self._name = ""
        self._wins = 0
        self._loses = 0

    def get_name(self):
        print
        self._name = input().upper()

    def inc_score(self, wlt):
        if wlt=="w": self._wins += 1
        elif wlt=="l": self._loses += 1
        elif wlt=="t": _tie += 1
        else: raise ValueError("Bad Input")

def player_num(): #Gets number of players
    while True:
        clear()
        endl(10)
        print("1 player or 2 players?")
        endl(5)
        pnum = input('Enter 1 or 2: '.rjust(55))
        try:
            assert int(pnum) == 1 or int(pnum) == 2
            clear()
            return int(pnum)
        except:
            print("\n\nPlease enter 1 or 2.")

def create_player():  #Creates players
    global p1
    p1 = Score()
    yield 0          #stops here if there is only 1 player
    global p2
    p2 = Score()

def pr_():          #testing object
    input(p1._wins)
    input(p2._wins)


for i in range(player_num()):
    create_player()
    input(p1)
input(p1._wins())
pr_()

无论我在哪里引用p1,我都应该获得必需的对象属性,但是我遇到了这个错误

Traceback (most recent call last):
  File "G:/Python/TicTacTwo.py", line 83, in <module>
    input(p1)
NameError: name 'p1' is not defined

2 个答案:

答案 0 :(得分:1)

您的问题不是global,而是yield中的create_player(),这会使函数变成生成器

可以做什么:

实际上是通过执行list(create_player())在生成器中运行的(不好,但是可行)。

但是我建议您改为重新设计代码,例如通过调用具有玩家人数的方法:

def create_player(num):  #Creates players
    if num >= 1:
        global p1
        p1 = Score()
    if num >= 2:
        global p2
        p2 = Score()

如果您解决此问题,下一个问题将是

1)input(p1)将打印p1的字符串表示形式,并且输入将丢失,您可能想要p1.get_name()

2)input(p1._wins())将提高TypeError: 'int' object is not callable

答案 1 :(得分:0)

我将重新设计该应用程序,以引入功能强大的python构造,这对进入OOP可能会有所帮助。

  1. 您的对象将代表玩家,然后不叫他们Score,不要叫他们Player
  2. 像这样使用_tie使其成为一个类变量,因此该值将为所有玩家共享。只有两个参与者,这可能是正确的,但是当您尝试扩展到更多玩家时,这会伤害您。保留它作为实例变量。
  3. 我是__slots__的粉丝。它是一类特殊变量,它告诉实例变量它们可以具有哪些属性。这将防止错误地插入新属性,并提高每个实例所需的内存,您可以删除此行,它会起作用,但我建议您保留它。 __slots__是任何可迭代的。我推荐使用元组,因为它们是不可变的。
  4. 属性也是一个非常好的功能。它们将充当实例属性,但是允许您指定获取值(a = instance.property),为其分配值(instance.property = value)或删除值(del instance.property)时的行为。 。名称似乎非常适合房地产。 getter将仅返回存储在_name中的值,setter将删除开头和结尾的空格,并将每个单词的首字母大写,而deletter将再次设置默认名称。
  5. 使用单个函数来计算结果不是很具描述性。让我们用3个函数来完成它。

代码看起来像这样:

# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"


class Player:

    # ( and ) are not needed but I'll keep them for clarity
    __slots__ = ("_name", "_wins", "_loses", "_ties")

    # We give a default name in case none is provided when the instance is built
    def __init__(self, name=DEFAULT_NAME):
        self._name = name
        self._wins = 0
        self._loses = 0
        self._ties = 0

    # This is part of the name property, more specifically the getter and the documentation
    @property
    def name(self):
        """ The name of the player """
        return self._name

    # This is the setter of the name property, it removes spaces with .strip() and
    # capitalizes first letters of each word with .title()
    @name.setter
    def name(self, name):
        self._name = name.strip().title()

    # This is the last part, the deleter, that assigns the default name again
    @name.deleter
    def name(self):
        self._name = DEFAULT_NAME

    def won(self):
        self._wins += 1

    def lost(self):
        self._loses += 1

    def tied(self):
        self._ties += 1

现在,这就是播放器本身所需要的。游戏应该在创建玩家的类别上有所不同。

class Game:

    _min_players = 1
    _max_players = 2

    def __init__(self, players):
        # Check that the number of players is correct
        if not(self._min_players <= players <= self._max_players):
            raise ValueError("Number of players is invalid")
        self._players = []
        for i in range(1, players+1):
            self._players.append(Player(input("Insert player {}'s name: ".format(i))))

    @property
    def players(self):
        # We return a copy of the list to avoid mutating the inner list
        return self._players.copy()

现在将按照以下方式创建游戏:

def new_game():
    return Game(int(input("How many players? ")))

此后,您将为游戏创建新的方法,例如进行比赛,从而调用玩家wonlosttied方法,等等。

我希望这里介绍的一些概念对您有用,例如属性,插槽,将对象创建委托给所有者对象等。