OOP(面向对象编程)

Classes(类的定义)

Classes are created using the keyword class and an indented block, which contains class methods (which are functions).

  1. class Cat:
  2. def __init__(self, color, legs):
  3. self.color = color
  4. self.legs = legs
  5. felix = Cat("ginger", 4)
  6. rover = Cat("dog-colored", 4)
  7. stumpy = Cat("brown", 3)

init

The init method is the most important method in a class.
This is called when an instance (object) of the class is created, using the class name as a function.(创建实例时调用, 常叫做构造函数)

All methods must have self as their first parameter, although it isn’t explicitly passed, Python adds the self argument to the list for you; you do not need to include it when you call the methods. Within a method definition, self refers to the instance calling the method.(self作为类中所有method暗含的第一个参数)

Instances of a class have attributes, which are pieces of data associated with them.(实例包含属性,即数据)
In this example, Cat instances have attributes color and legs. These can be accessed by putting a dot, and the attribute name after an instance.
In an init method, self.attribute can therefore be used to set the initial value of an instance’s attributes.

  1. class Cat:
  2. def __init__(self, color, legs):
  3. self.color = color
  4. self.legs = legs
  5. felix = Cat("ginger", 4)
  6. print(felix.color)

Method

Classes can have other methods defined to add functionality to them.
Remember, that all methods must have self as their first parameter.
These methods are accessed using the same dot syntax as attributes.

  1. class Cat:
  2. def __init__(self, color, legs):
  3. self.color = color
  4. self.legs = legs
  5. def bark(self):
  6. print("miao!")
  7. felix = Cat("ginger", 4)
  8. felix.bark() #调用方法
  • Note: Trying to access an attribute of an instance that isn’t defined causes an AttributeError. This also applies when you call an undefined method.

Inheritance(继承)

  • Inheritance provides a way to share functionality between classes.
    Imagine several classes, Cat, Dog, Rabbit and so on. Although they may differ in some ways (only Dog might have the method bark), they are likely to be similar in others (all having the attributes color and name).
    This similarity can be expressed by making them all inherit from a superclass Animal, which contains the shared functionality.
    To inherit a class from another class, put the superclass name in parentheses after the class name.
  1. class Animal:
  2. def __init__(self, name, color):
  3. self.name = name
  4. self.color = color
  5. class Cat(Animal):
  6. def purr(self):
  7. print("Purr...")
  8. class Dog(Animal):
  9. def bark(self):
  10. print("Woof!")
  11. fido = Dog("Fido", "brown")
  12. print(fido.color)
  13. fido.bark()
  • A class that inherits from another class is called a subclass(子类).
    A class that is inherited from is called a superclass(父类).
    If a class inherits from another with the same attributes or methods, it overrides them.

  • Inheritance can also be indirect. One class can inherit from another, and that class can inherit from a third class.(即子类的子类也成立,但不能循环)

  • The function super is a useful inheritance-related function that refers to the parent class. It can be used to find the method with a certain name in an object’s superclass.(使用父类的method) ```python class A: def spam(self):

    1. print(1)

class B(A): def spam(self): print(2) super().spam() # calls the spam method of the superclass.

B().spam()

  1. <a name="c27a7fd4"></a>
  2. ## Magic Methods & Operator Overloading
  3. <a name="f4fde1ca"></a>
  4. ### Magic Method
  5. **Magic methods** are special methods which have **double underscores** (双下划线)at the beginning and end of their names.<br />
  6. They are also known as **dunders**.<br />
  7. So far, the only one we have encountered is **__init__**, but there are several others.<br />
  8. They are used to create functionality that can't be represented as a normal method.(用于非常规用法)
  9. One common use of them is **operator overloading**.(运算符重载是dunders的用途之一)<br />
  10. This means defining operators for custom classes that allow operators such as + and * to be used on them.<br />
  11. An example magic method is **__add__** for +.
  12. ```python
  13. class Vector2D:
  14. def __init__(self, x, y):
  15. self.x = x
  16. self.y = y
  17. def __add__(self, other):
  18. return Vector2D(self.x + other.x, self.y + other.y)
  19. first = Vector2D(5, 7)
  20. second = Vector2D(3, 9)
  21. result = first + second
  22. print(result.x)
  23. print(result.y)

More magic methods for common operators:

  • sub for -
  • mul for *
  • truediv for /
  • floordiv for //
  • mod for %
  • pow for **
  • and for &
  • xor for ^
  • or for |

The expression x + y is translated into x.add(y).
However, if x hasn’t implemented add_, and x and y are of different types, then y.radd(x) is called.
There are equivalent r methods for all magic methods just mentioned.
Example:

  1. class SpecialString:
  2. def __init__(self, cont):
  3. self.cont = cont
  4. def __truediv__(self, other):
  5. line = "=" * len(other.cont)
  6. return "\n".join([self.cont, line, other.cont])
  7. spam = SpecialString("spam")
  8. hello = SpecialString("Hello world!")
  9. print(spam / hello)

Python also provides magic methods for comparisons.

  • lt for <
  • le for <=
  • eq for ==
  • ne for !=
  • gt for >
  • ge for >=

If ne is not implemented, it returns the opposite of eq.

Example:

  1. class SpecialString:
  2. def __init__(self, cont):
  3. self.cont = cont
  4. def __gt__(self, other):
  5. for index in range(len(other.cont)+1):
  6. result = other.cont[:index] + ">" + self.cont
  7. result += ">" + other.cont[index:]
  8. print(result)
  9. spam = SpecialString("spam")
  10. eggs = SpecialString("eggs")
  11. spam > eggs

There are no other relationships between the other operators.There are several magic methods for making classes act like containers.

  • len for len()

  • getitem for indexing 即索引取值,重载data[key]

  • setitem for assigning to indexed values 当期望定义的类具备按照键存储值时,即类能够执行data[‘key’]=value

  • delitem for deleting indexed values 这个方法在对对象的组成部分使用del语句的时候被调用,应删除与key相关联的值

  • iter for iteration over objects (e.g., in for loops) 迭代器,生成迭代对象时调用,返回值必须是对象自己,然后for可以循环调用next方法
    next():每一次for循环都调用该方法(必须存在)

  • contains for in

There are many other magic methods that we won’t cover here, such as call for calling objects as functions, and int, str, and the like, for converting objects to built-in types.
Example:

  1. import random
  2. class VagueList:
  3. def __init__(self, cont):
  4. self.cont = cont
  5. **def __getitem__(self, index):
  6. return self.cont[index + random.randint(-1, 1)]
  7. def __len__(self):
  8. return random.randint(0, len(self.cont)\*2)**
  9. vague_list = VagueList(["A", "B", "C", "D", "E"])
  10. print(len(vague_list))
  11. print(len(vague_list))
  12. print(vague_list[2])
  13. print(vague_list[2])

Object Lifecycle(对象生命周期)

The lifecycle of an object is made up of its creation, manipulation, and destruction.

The first stage of the life-cycle of an object is the definition of the class to which it belongs.
The next stage is the instantiation of an instance, when init_ is called. Memory is allocated to store the instance. Just before this occurs, the new__ method of the class is called. This is usually overridden only in special cases.
After this has happened, the object is ready to be used.

Other code can then interact with the object, by calling functions on it and accessing its attributes.
Eventually, it will finish being used, and can be destroyed.

When an object is destroyed, the memory allocated to it is freed up, and can be used for other purposes.
Destruction of an object occurs when its reference count reaches zero. Reference count is the number of variables and other elements that refer to an object. 引用计数记录指向对象引用的个数,当变为0,则被释放。总结了引用计数的注意点和如何使用。使用引用计数的唯一理由就是只要还有变量指向就应当阻止对象被释放
If nothing is referring to it (it has a reference count of zero) nothing can interact with it, so it can be safely deleted.

In some situations, two (or more) objects can be referred to by each other only, and therefore can be deleted as well.
The del statement reduces the reference count of an object by one, and this often leads to its deletion.
The magic method for the del statement is _del.
The process of deleting objects when they are no longer needed is called garbage collection.
In summary, an object’s reference count increases when it is assigned a new name or placed in a container (list, tuple, or dictionary). The object’s reference count decreases when it’s deleted with del, its reference is reassigned, or its reference goes out of scope. When an object’s reference count reaches zero, Python automatically deletes it.
Example:

  1. a = 42 # Create object <42>
  2. b = a # Increase ref. count of <42>
  3. c = [a] # Increase ref. count of <42>
  4. del a # Decrease ref. count of <42>
  5. b = 100 # Decrease ref. count of <42>
  6. c[0] = -1 # Decrease ref. count of <42>

Data Hiding(数据隐藏,封装)

  • A key part of object-oriented programming is encapsulation(封装), which involves packaging of related variables and functions into a single easy-to-use object - an instance of a class.
    A related concept is data hiding, which states that implementation details of a class should be hidden, and a clean standard interface be presented for those who want to use the class.
    In other programming languages, this is usually done with private methods and attributes, which block external access to certain methods and attributes in a class.(其它语言通常使用私有化实现)
    The Python philosophy is slightly different. It is often stated as “we are all consenting adults here”, meaning that you shouldn’t put arbitrary restrictions on accessing parts of a class. Hence there are no ways of enforcing a method or attribute be strictly private.
    However, there are ways to discourage people from accessing parts of a class, such as by denoting that it is an implementation detail, and should be used at their own risk.

  • (弱私有方法)Weakly private methods and attributes have a single underscore(单下划线) at the beginning.
    This signals that they are private, and shouldn’t be used by external code. However, it is mostly only a convention, and does not stop external code from accessing them.(只是约定,仍能从外部调用)
    Its only actual effect is that from module_name import * won’t import variables that start with a single underscore.(模块调用不会导入单下划线开头的函数)
    Example:

  1. class Queue:
  2. def __init__(self, contents):
  3. self._hiddenlist = list(contents)
  4. def push(self, value):
  5. self._hiddenlist.insert(0, value)
  6. def pop(self):
  7. return self._hiddenlist.pop(-1)
  8. def __repr__(self):
  9. return "Queue({})".format(self._hiddenlist)
  10. queue = Queue([1, 2, 3])
  11. print(queue)
  12. queue.push(0)
  13. print(queue)
  14. queue.pop()
  15. print(queue)
  16. print(queue._hiddenlist) #仍可以调用

In the code above, the attribute _hiddenlist is marked as private, but it can still be accessed in the outside code.
The _repr magic method is used for string representation of the instance.(实例的字符串表示方法)

  • (强私有方法)Strongly private methods and attributes have a double underscore(双下划线) at the beginning of their names. This causes their names to be mangled, which means that they can’t be accessed from outside the class.
    The purpose of this isn’t to ensure that they are kept private, but to avoid bugs if there are subclasses that have methods or attributes with the same names.(避免在子类有相同method或attributes时出现bug)
    Name mangled methods can still be accessed externally, but by a different name. The method privatemethod of class Spam could be accessed externally with _Spamprivatemethod.(注意:调用的方法与弱私有不同)
    Example: ```python class Spam: egg = 7 def print_egg(self): print(self.egg)

s = Spam() s.print_egg() print(s._Spamegg) print(s.egg)

  1. <br />Basically, Python protects those members by internally changing the name to include the class name.
  2. <a name="61a61f57"></a>
  3. ## Class & Static Methods
  4. <a name="4292c1c1"></a>
  5. ### Class method(类方法)
  6. Methods of objects we've looked at so far are called by an instance of a class, which is then passed to the **self** parameter of the method.<br />
  7. **Class methods** are different - they are called by a class, which is passed to the **cls** parameter of the method.<br />
  8. A common use of these are factory methods, which instantiate an instance of a class, using different parameters than those usually passed to the class constructor.<br />
  9. Class methods are marked with a **classmethod decorator**.
  10. 类方法不通过创建实例调用,可以直接通过类名调用,需要**cls**参数,同时定义时用@classmethod修饰
  11. **Example:**
  12. ```python
  13. class Rectangle:
  14. def __init__(self, width, height):
  15. self.width = width
  16. self.height = height
  17. def calculate_area(self):
  18. return self.width * self.height
  19. @classmethod
  20. def new_square(cls, side_length):
  21. return cls(side_length, side_length)
  22. square = Rectangle.new_square(5)
  23. print(square.calculate_area())

new_square is a class method and is called on the class, rather than on an instance of the class. It returns a new object of the class cls.

Technically, the parameters self and cls are just conventions; they could be changed to anything else. However, they are universally followed, so it is wise to stick to using them.(都只是习惯定义,建议使用)

Static Method(静态方法)

Static methods are similar to class methods, except they don’t receive any additional arguments; they are identical to normal functions that belong to a class.
They are marked with the staticmethod decorator.
Example:

  1. class Pizza:
  2. def __init__(self, toppings):
  3. self.toppings = toppings
  4. @staticmethod
  5. def validate_topping(topping): # 不使用self参数
  6. if topping == "pineapple":
  7. raise ValueError("No pineapples!")
  8. else:
  9. return True
  10. ingredients = ["cheese", "onions", "spam"]
  11. if all(Pizza.validate_topping(i) for i in ingredients):
  12. pizza = Pizza(ingredients)

Static methods behave like plain functions, except for the fact that you can call them from an instance of the class.

Properties

Properties provide a way of customizing access to instance attributes.
They are created by putting the property decorator above a method, which means when the instance attribute with the same name as the method is accessed, the method will be called instead.
One common use of a property is to make an attribute read-only.
Example:

  1. class Pizza:
  2. def __init__(self, toppings):
  3. self.toppings = toppings
  4. @property
  5. def pineapple_allowed(self):
  6. return False
  7. pizza = Pizza(["cheese", "tomato"])
  8. print(pizza.pineapple_allowed)
  9. pizza.pineapple_allowed = True

类似函数作为属性成员

Properties can also be set by defining setter/getter functions.
The setter function sets the corresponding property’s value.

setter:赋值

The getter gets the value.

getter:获取值

To define a setter, you need to use a decorator of the same name as the property, followed by a dot and the setter keyword.
The same applies to defining getter functions.

Example:

  1. class Pizza:
  2. def __init__(self, toppings):
  3. self.toppings = toppings
  4. self._pineapple_allowed = False
  5. @property
  6. def pineapple_allowed(self):
  7. return self._pineapple_allowed
  8. @pineapple_allowed.setter
  9. def pineapple_allowed(self, value):
  10. if value:
  11. password = input("Enter the password: ")
  12. if password == "Sw0rdf1sh!":
  13. self._pineapple_allowed = value
  14. else:
  15. raise ValueError("Alert! Intruder!")
  16. pizza = Pizza(["cheese", "tomato"])
  17. print(pizza.pineapple_allowed)
  18. pizza.pineapple_allowed = True
  19. print(pizza.pineapple_allowed)

Simple Game

  1. def get_input(): #输入指令函数
  2. command = input(": ").split() #.split()分割输入内容,默认空格为分隔符
  3. verb_word = command[0]
  4. if verb_word in verb_dict:
  5. verb = verb_dict[verb_word]
  6. else:
  7. print("Unknown verb {}". format(verb_word))
  8. return
  9. if len(command) >= 2:
  10. noun_word = command[1]
  11. print (verb(noun_word))
  12. else:
  13. print(verb("nothing"))
  14. def say(noun):
  15. return 'You said "{}"'.format(noun)
  16. verb_dict = {
  17. "say": say,
  18. } #这是一个字典
  19. while True:
  20. get_input()

The code above takes input from the user, and tries to match the first word with a command in verb_dict. If a match is found, the corresponding function is called.

The next step is to use classes to represent game objects.

  1. class GameObject: #类游戏目标
  2. class_name = "" #类名
  3. desc = "" #类的描述
  4. objects = {} #以字典储存,class_name为索引,类实例为储存数据
  5. def __init__(self, name): #构造函数
  6. self.name = name
  7. GameObject.objects[self.class_name] = self
  8. def get_desc(self): #得到类的描述
  9. return self.class_name + "\n" + self.desc
  10. class Goblin(GameObject): #子类
  11. class_name = "goblin"
  12. desc = "A foul creature"
  13. goblin = Goblin("Gobbly") #实例化
  14. def examine(noun): #检测目标是否存在,存在则输出其描述
  15. if noun in GameObject.objects:
  16. return GameObject.objects[noun].get_desc()
  17. else:
  18. return "There is no {} here.".format(noun)

We created a Goblin class, which inherits from the GameObjects class.
We also created a new function examine, which returns the objects description.
Now we can add a new “examine” verb to our dictionary and try it out!

  1. verb_dict = {
  2. "say": say,
  3. "examine": examine,
  4. }

Combine this code with the one in our previous example, and run the program.

  1. >>>
  2. : say Hello!
  3. You said "Hello!"
  4. : examine goblin
  5. goblin
  6. A foul creature
  7. : examine elf
  8. There is no elf here.
  9. :

This code adds more detail to the Goblin class and allows you to fight goblins.

  1. class Goblin(GameObject):
  2. def __init__(self, name):
  3. self.class_name = "goblin"
  4. self.health = 3
  5. self._desc = " A foul creature"
  6. super().__init__(name) #继承父类method,不会覆盖
  7. @property
  8. def desc(self):
  9. if self.health >=3:
  10. return self._desc
  11. elif self.health == 2:
  12. health_line = "It has a wound on its knee."
  13. elif self.health == 1:
  14. health_line = "Its left arm has been cut off!"
  15. elif self.health <= 0:
  16. health_line = "It is dead."
  17. return self._desc + "\n" + health_line
  18. @desc.setter
  19. def desc(self, value):
  20. self._desc = value
  21. def hit(noun): #攻击函数
  22. if noun in GameObject.objects: #检测是否有攻击的对象
  23. thing = GameObject.objects[noun]
  24. if type(thing) == Goblin: #检测攻击的对象是否是Goblin
  25. thing.health = thing.health - 1
  26. if thing.health <= 0:
  27. msg = "You killed the goblin!"
  28. else:
  29. msg = "You hit the {}".format(thing.class_name)
  30. else:
  31. msg ="There is no {} here.".format(noun)
  32. return msg

This was just a simple sample.
You could create different classes (e.g., elves, orcs, humans), fight them, make them fight each other, and so on.