简体   繁体   中英

Why is my instance counter display 0 in this Python code?

I made a simple code to demonstrate and understand classes - however when I run this, my lists show that they are empty, containing "None" values instead of the strings that the user enters as names.

#Static methods do not require the object to be initiated. Can be remotely accessed from outside the function .

#Counting critters and remote access.

class Orc (object):

    total = 0       

    def get_score (self):
        print "The number of orcs the orc factory has made is",Orc.total   

    def __init__ (self):
        Orc.total += 1
        name =  raw_input ("I am a critter by the name of:\n")


#Creating 10 Orcs 


list = []

for i in range (4):    list[i] = list.append(Orc.get_score(Orc()))  

print "You have created 4 Orcs!" print "The name of your first orc is",list[0] print "The name of your fourth orc is", list[3]

There are a few errors in your code. First in the way you use lists. Second, in the way you call methods on your objects. The combination of errors explains why you have a list of None at the end.

List name

list = []

Don't name a list list . It is already the name of, well..., the list class, ie in Python you can do my_list = [] or my_list = list() with the exact same effect.

You want to call your list something meaningful, like orc_list

List Insertion

for i in range (4):
    orc_list[i] = orc_list.append(...)

orc_list.append does what it says: it appends an element to the given list. However, it does not return anything. So what your code is doing is

  1. taking an empty list
  2. setting i to 0
  3. inserting what you pass to append at the end of the list
  4. inserting None at index i and thus overriding what you did in 3.
  5. incrementing i
  6. going back to 3.

You want to simply use orc_list.append(...)

Method Call

Orc.get_score(Orc())

I imagine you are getting confused by the self argument. In a class, Python will automatically pass the instance you are working on as the self argument. You don't need to provide that argument.

You want to write

Orc().get_score()

This creates an Orc object, and then calls get_score on it. Python 'injects' the Orc instance you have created into get_score for you.

Method Return

We're now down to

orc_list.append(Orc().get_score())

which is equivalent to

score = Orc().get_score()
orc_list.append(score)

The problem is that there is no return statement in get_score . This means that python will return None when you call that method. Which means that you are appending None to your list.

You want to have

def get_score(self):
    print "The number of orcs the orc factory has made is", Orc.total
    return Orc.total

Static behaviour

If you really wanted to have a method not bound to an instance of the Orc class, you could use either a class method or a static method .

In your case, you do not need to do anything to the class object, so your choice would be to use a static method.

You would declare

@staticmethod
def get_score():
    print "The number of orcs the orc factory has made is", Orc.total

You would then call that method using Orc.get_score()

To define a class method in Python, use classethod decorator and call the first parameter cls

class Orc(object):

    total = 0       

    @classmethod # this will make the method a class method
    def get_score (cls): # convention is then to call the 1st param 'cls'
        print "The number of orcs the orc factory has made is", cls.total   

    def __init__ (self):
        Orc.total += 1
        # use self is you want' to register a name
        # however putting a raw_input in an __init__ is NOT recommanded
        # you should pass name as a parameter 
        # and call the raw_input in the for loop
        self.name =  raw_input ("I am a critter by the name of:\n")

orcs = [] # don't call your lists 'list' because `list` is standard Python function

for i in range(4): # put this on two lines for clarity or use a comprehension list
   orcs.append(Orc())  

print "You have created 4 Orcs!" 
print "The name of your first orc is", orcs[0].name # if you don't use `name`, you will see the reference of the object

print "The name of your fourth orc is", orcs[3].name

A cleaner version (something you should aim for):

class Orc(object):

    total = 0       

    @classmethod # 
    def get_instances_count(cls):
        """
            Return the number or orcs that have been instanciated 
        """
        # ^ Put some documentation below your method
        # these are called "docstring" and are detected by Python

        # you should return values in method rather than print
        # there are rare cases when you do want print, but when you'll
        # encounter them, you won't need me to correct your code anymore
        return cls.total   

    def __init__ (self, name):
        Orc.total += 1
        self.name = name # we get the name as a parameter


l = []

for i in range(4): # put this on two lines for clarity or use a comprehension list
   orc = Orc(raw_input("Enter a name:\n"))
   l.append(orc)  

print "You have created %s Orcs!" % Orc.get_instances_count()
print "The name of your first orc is", l[0].name #

print "The name of your fourth orc is", l[3].name

Now the more Pythonic version (something you should be able to do once used to Python):

class Orc(object):

    total = 0       

    # you don't need accessors in Python: most things are public anyway
    # and you got property

    def __init__ (self, name):
        Orc.total += 1
        self.name = name # we get the name as a parameter

    def __str__(self):
      # this will be call when printing an orc
      return self.name

# list comprehension are quick and clean ways to create lists
# give a real name to your list
orcs = [Orc(raw_input("Enter a name:\n")) for i in range(4)]

# using parenthesis for `print` is a good habit to take with then incoming Python 3
print("You have created %s Orcs!" % Orc.total)
for i, orc in enumerate(orcs):
  print("Orc #%s is %s" % (i, orc))

list.append returns a None value, so it essentially never makes sense to assign its result to anything. You call append for the side-effects, ie, to have it put a value at the end of the list. Like this:

for i in range (4):    
    list.append(Orc.get_score(Orc()))

I don't think the Orc.get_score(Orc()) is what you want, either: it also returns None instead of a score and the method call is technically correct but unlikely to be what you really intend.

Why should there be something in your list?

you do:

list.append(Orc.get_score(Orc())

which is equivalent to:

item_to_add = Orc.get_score(Orc())
list.append(item_to_add)

Your method Orc.get_score has no return statement, so it returns None . therefore, item_to_add will be None, and None will be appended to your list.

As a side note: python is not java. Dont use classes just to use classes. Use classes, when you want to follow OO-Pradigma, ie sending messages to objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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