python3进阶-微课学习-第二章面向对象编程

本文介绍了Python中的面向对象编程概念,包括类的创建和实例化,__init__构造方法,属性和方法的使用,以及继承、魔术方法、数据隐藏、类方法、静态方法和属性的概念。文章通过示例展示了如何定义和使用这些概念,强调了在Python中实现对象行为和数据封装的方式。
摘要由CSDN通过智能技术生成

我们先前看过两种编程模式 - 命令式(使用语句,循环和函数)和 函数式(使用纯函数,高阶函数和递归)。

另一个非常流行的范例是面向对象编程(OOP)。

对象是使用类来创建的,而这些类实际上是 OOP 的模具。

这个类描述了这个对象是什么,但是和对象本身是分开的。换句话说,一个类可以被描述为一个对象的蓝图,描述或定义。

您可以使用相同的类作为创建多个不同对象的蓝图。

类是使用关键字 class 和一个包含类方法的缩进块创建的。

下面是一个简单的类和它的对象的例子

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

print("felix:",felix.__dict__) #dict是用来存储对象属性的一个字典,其键为属性名,值为属性的值.
print("rover:",rover.__dict__)
print("stumpy:",stumpy.__dict__)

这段代码定义了一个名为 Cat 的类,它有两个属性:color 和 legs。
然后这个类被用来创建这个类的3个独立的对象

在这里插入图片描述

__ init __

_ _ init __ 方法是一个类中最重要的方法。

这是在创建类的实例(对象)时使用类名称作为函数调用的。

所有的方法都必须以 self 作为自己的第一个参数,虽然它没有被明确地传递,但是 Python 为自己添加了自变量;

在调用方法时,不需要包含它。在一个方法定义中,self 指的是调用该方法的实例

注意: init 是两个下划线, 不是一个下划线 _ init _

类的实例具有属性,这些属性是与它们相关联的数据片段。

在这个例子中,Cat 实例具有属性 color 和 legs。这些可以通过在一个实例之后加一个点和属性名来访问。

在 __ init__ 方法中,可以使用 self.attribute 来设置实例属性的初始值。

例如:

class Cat:
  def __init__(self, color, legs):
    self.color = color
    self.legs = legs

felix = Cat("ginger", 4)
print(felix.color)

ginger

在上面的例子中,__ init__ 方法接受两个参数并将它们分配给对象的属性。init 方法被称为类构造函数
在这里插入图片描述

方法

类可以定义方法来为其添加功能。

请记住,所有的方法必须有 self 作为他们的第一个参数。

这些方法使用与属性相同的点语法进行访问。

例如:

class Dog:
  def __init__(self, name, color):
    self.name = name
    self.color = color

  def bark(self):
    print("Woof!")

fido = Dog("Fido", "brown")
print(fido.name)
fido.bark()

Fido
Woof!

类还可以具有通过在类的主体内分配变量而创建的类属性。这些可以从类的实例或类本身访问。

例如:

class Dog:
  legs = 4
  def __init__(self, name, color):
    self.name = name
    self.color = color

fido = Dog("Fido", "brown")
print(fido.legs)
print(Dog.legs)

4
4

类属性由类的所有实例共享
在这里插入图片描述
尝试访问未定义实例的属性会导致 AttributeError。这也适用于你调用未定义的方法。

例如:

class Rectangle: 
  def __init__(self, width, height):
    self.width = width
    self.height = height

rect = Rectangle(7, 8)
print(rect.color)

AttributeError: ‘Rectangle’ object has no attribute ‘color’

在这里插入图片描述

继承

继承提供了一种在类之间共享功能的方法。

想象几个类,Cat,Dog,Rabbit等。虽然它们在某些方面可能有所不同(只有 Dog 可能有 bark 方法),但它们可能在其他方面相似(都具有 color 和 name 的属性)。

这种相似性可以通过使它们全部从包含共享功能的超类 Animal 中继承来表示。

要从另一个类继承一个类,请将超类名放在类名后面的括号中

class Animal: 
  def __init__(self, name, color):
    self.name = name
    self.color = color

class Cat(Animal):
  def purr(self):
    print("Purr...")
        
class Dog(Animal):
  def bark(self):
    print("Woof!")

fido = Dog("Fido", "brown")
print(fido.color)
fido.bark()

brown
Woof!

在这里插入图片描述
从另一个类继承的类称为子类。

被继承的类被称为超类。

如果一个类继承了另一个具有相同属性或方法的类,它的属性和方法将覆盖它们

class Wolf: 
  def __init__(self, name, color):
    self.name = name
    self.color = color

  def bark(self):
    print("Grr...")

class Dog(Wolf):
  def bark(self):
    print("Woof")
        
husky = Dog("Max", "grey")
husky.bark()
Woof

在上面的例子中,Wolf 是超类,Dog 是子类
在这里插入图片描述
继承也可以是间接的。一个类B继承类A,而类C也可以继承类B

class A:
  def method(self):
    print("A method")
    
class B(A):
  def another_method(self):
    print("B method")
    
class C(B):
  def third_method(self):
    print("C method")
    
c = C()
c.method()
c.another_method()
c.third_method()
A method
B method
C method

但是,不允许循环继承
在这里插入图片描述
super 函数是一个与父类继承相关的函数。它可以用来在对象的超类中找到具有特定名称的方法。

例如:

class A:
  def spam(self):
    print(1)

class B(A):
  def spam(self):
    print(2)
    super().spam()
            
B().spam()
2
1  

super().spam() 是调用超类的 spam 方法
在这里插入图片描述

魔术方法

魔术方法是在名称的开始和结尾都有双下划线的特殊方法。
到目前为止,我们唯一遇到的是 __ init__,但还有其他几个。
它们被用来创建不能用普通方法表示的功能。

它们的一个常见用途是运算符重载。
这意味着为自定义类定义运算符,允许使用 + 和 * 等运算符。
例子中魔术方法是 __ add__ 重载 +

class Vector2D:
  def __init__(self, x, y):
    self.x = x
    self.y = y
  def __add__(self, other):
    return Vector2D(self.x + other.x, self.y + other.y)

first = Vector2D(5, 7)
second = Vector2D(3, 9)
result = first + second
print(result.x)
print(result.y)
8
16 

__ add__ 方法允许为我们的类中的 + 运算符定义自定义行为。
正如你所看到的,它添加了对象的相应属性并返回一个包含结果的新对象。
一旦定义了,我们就可以将这个类的两个对象相加

常见的魔术方法: 

__sub__ 对应 -
__mul__ 对应 *
__truediv__ 对应 /
__floordiv__ 对应 //
__mod__ 对应 %
__pow__ 对应 **
__and__ 对应 &
__xor__ 对应 ^
__or__ 对应 |

注意: 名字前后是双下划线
表达式 x + y 被翻译成 x.__ add__(y)。
然而,如果 x 没有实现 __ add__,并且 x 和 y 的类型不同,则调用 y.__ radd__(x)。
对于刚刚提到的所有魔法方法,都有等价的方法。
例如:

class SpecialString:
  def __init__(self, cont):
    self.cont = cont

  def __truediv__(self, other):
    line = "=" * len(other.cont)
    return "\n".join([self.cont, line, other.cont])

spam = SpecialString("spam")
hello = SpecialString("Hello world!")
print(spam / hello)
spam
============
Hello world!

在上面的例子中,我们为我们的类 SpecialString 定义了除法操作
在这里插入图片描述

Python 也为比较运算提供了魔术方法。

__lt__ 对应 <
__le__ 对应 <=
__eq__ 对应 ==
__ne__ 对应 !=
__gt__ 对应 >
__ge__ 对应 >=

如果 __ ne__ 没有被实现,它将返回 __ eq__ 相反的结果。

其他比较运算符之间没有其他关系。

例如:

class SpecialString:
  def __init__(self, cont):
    self.cont = cont

  def __gt__(self, other):
    for index in range(len(other.cont)+1):
      result = other.cont[:index] + ">" + self.cont
      result += ">" + other.cont[index:]
      print(result)

spam = SpecialString("spam")
eggs = SpecialString("eggs")
spam > eggs
>spam>eggs
e>spam>ggs
eg>spam>gs
egg>spam>s
eggs>spam>

可以为重载操作符定义任何自定义行为

有几个神奇的方法使类像容器一样行事。

__len__ 对应 len()
__getitem__ 对应 获取索引
__setitem__ 对应 分配索引值
__delitem__ 对应 删除索引值
__iter__ 对应 迭代对象(例如for循环)
__contains__ 对应 in

还有很多其他的魔术方法,我们不会在这里介绍,比如将 __ call__ 作为函数调用对象,__ int__,__ str__ 等等,将对象转换为内建类型。

例如:

import random

class VagueList:
  def __init__(self, cont):
    self.cont = cont

  def __getitem__(self, index):
    return self.cont[index + random.randint(-1, 1)]

  def __len__(self):
    return random.randint(0, len(self.cont)*2)

vague_list = VagueList(["A", "B", "C", "D", "E"])
print(len(vague_list))
print(len(vague_list))
print(vague_list[2])
print(vague_list[2])
6
7
D
C

我们已经重写了类 VagueList 的 len() 函数来返回一个随机数。
索引函数还根据表达式从列表中返回一个范围内的随机项
在这里插入图片描述

对象生命周期

对象的生命周期由对象的创建,操作和销毁几个部分组成。

对象的生命周期

第一阶段是它所属的类的定义。

下一个阶段是调用 __ init__ 时实例的实例化。内存被分配来存储实例。

在调用 __ init__ 方法之前,Python首先调用 _ new_ 方法。

这之后,对象就可以使用了
其他代码则可以通过调用对象和访问其属性来与对象交互。
最终,对象会完成使用,并可以被销毁
在这里插入图片描述
当一个对象被销毁时,分配给它的内存被释放,并可用于其他目的。

当引用计数达到零时,就会发生对象的破坏。引用计数是引用一个对象的变量和其他元素的数量。

如果什么都没有引用它(它的引用计数为零),什么都不能与它交互,所以它可以安全地删除。

在某些情况下,两个(或更多)对象只能被彼此引用,因此也可以被删除。

del 语句会将对象的引用计数减少一个,这通常会导致删除。

del 语句的魔术方法是__ del__。

不再需要的对象删除的过程称为垃圾收集。

总之,当一个对象的引用计数被分配一个新的名字或者放在一个容器(列表,元组或者字典)中时,它的引用计数会增加。

当用 del 删除对象的引用计数时,它的引用计数会减少,引用被重新分配或引用超出作用域。当一个对象的引用计数达到零时,Python 会自动删除它。

例如:

a = 42  # 创建对象 <42>
b = a  # 计数器计数增加
c = [a]  # 计数器计数增加

del a  # 计数器计数减少
b = 100  # 计数器计数减少
c[0] = -1  # 计数器计数减少

在这里插入图片描述

数据隐藏

面向对象编程的一个关键部分是封装,它涉及将相关的变量和函数打包到一个简单易用的对象中 - 一个类的实例。

一个相关的概念是数据隐藏,它指出一个类的实现细节应该被隐藏,并且为那些想要使用这个类的用户提供一个干净的标准接口。

在其他编程语言中,这通常使用私有方法和属性来完成,这些私有方法和属性阻止对类中某些方法和属性被外部访问。

Python 略有不同。没有对任何一个阶级的部分进行任意的限制。因此,没有办法强制一个方法或属性是严格私密的
但是,有些方法可以阻止人们访问某个类的某些部分
在这里插入图片描述
弱的私有方法和属性在开头只有一个下划线。

这表示它们是私有的,不应该被外部代码使用。但是,它大多只是一个约定,并不会阻止外部代码访问它们。

它唯一的实际效果是 from 模块名 import * 不会导入以单个下划线开头的变量。

例如:

class Queue:
  def __init__(self, contents):
    self._hiddenlist = list(contents)

  def push(self, value):
    self._hiddenlist.insert(0, value)
   
  def pop(self):
    return self._hiddenlist.pop(-1)

  def __repr__(self):
    return "Queue({})".format(self._hiddenlist)

queue = Queue([1, 2, 3])
print(queue)
queue.push(0)
print(queue)
queue.pop()
print(queue)
print(queue._hiddenlist)
Queue([1, 2, 3])
Queue([0, 1, 2, 3])
Queue([0, 1, 2])
[0, 1, 2]

在上面的代码中,hiddenlist 属性被标记为私有的,但仍然可以在外部代码中访问。
__ repr
_ 魔术方法用于实例的字符串表示
在这里插入图片描述
强私有方法和属性在名称的开始处有一个双下划线。这导致他们的名字错位,这意味着他们不能从类外访问。

这样做的目的不是确保它们保持私有,而是为了避免错误,如果存在具有相同名称的方法或属性的子类时。

名称 错位 方法仍然可以在外部访问,但是以不同的名称访问。 Spam 类的 __private 方法可以通过 _Spam__private 方法外部访问。

例如:

class Spam:
  __egg = 7
  def print_egg(self):
    print(self.__egg)

s = Spam()
s.print_egg()
print(s._Spam__egg)
print(s.__egg)
7
7
AttributeError: 'Spam' object has no attribute '__egg'

Python 通过内部更改名称来包含类名来保护这些成员
在这里插入图片描述

类方法

到目前为止,我们所看到的对象的方法被一个类的实例所调用,然后被传递给方法的 self 参数。

类方法是不同的 - 它们被一个类所调用,类方法传递的 参数是 cls 。

类方法用 classmethod 装饰器标记。

例如:

class Rectangle:
  def __init__(self, width, height):
    self.width = width
    self.height = height

  def calculate_area(self):
    return self.width * self.height

  @classmethod
  def new_square(cls, side_length):
    return cls(side_length, side_length)

square = Rectangle.new_square(5)
print(square.calculate_area())

25

new_square 是一个类方法,在类上调用,而不是在类的实例上调用。它返回类 cls 的新对象
从技术上说,参数 self 和 cls 只是惯例; 他们可以改变为其他任何东西。然而,它们是普遍被遵循的,所以坚持使用它们是明智的
在这里插入图片描述

静态方法

静态方法与类方法类似,只是它们没有任何额外的参数。

它们用 staticmethod 装饰器标记。

例如:

class Pizza:
  def __init__(self, toppings):
    self.toppings = toppings

  @staticmethod
  def validate_topping(topping):
    if topping == "pineapple":
      raise ValueError("No pineapples!")
    else:
      return True

ingredients = ["cheese", "onions", "spam"]
if all(Pizza.validate_topping(i) for i in ingredients):
  pizza = Pizza(ingredients) 

除了可以从类的一个实例调用它们之外,静态方法的行为与纯函数类似
在这里插入图片描述
静态方法是绑定到类而不是类的对象的方法。它不需要任何实例来执行某些工作。它可以使用类名进行调用。

根据这个定义,最有可能成为静态方法的选项是:

def spam(cls, x, y)

属性

属性提供了一种自定义实例属性访问的方法。

它们是通过将属性装饰器放在一个方法上面创建的,这意味着当访问与方法同名的实例属性时,方法将被调用。

属性的一种常见用法是使属性为只读。

例如:

class Pizza:
  def __init__(self, toppings):
    self.toppings = toppings
    
  @property
  def pineapple_allowed(self):
    return False

pizza = Pizza(["cheese", "tomato"])
print(pizza.pineapple_allowed)
pizza.pineapple_allowed = True

False
AttributeError: can’t set attribute

在这里插入图片描述
属性也可以通过定义 setter/getter 函数来设置。

setter 函数设置相应的属性值。

getter 函数获取相应的属性值。

要定义一个 setter,你需要使用一个与属性相同名字的装饰器,后面跟着 .setter。

这同样适用于定义 getter 函数。

例如:

class Pizza:
  def __init__(self, toppings):
    self.toppings = toppings
    self._pineapple_allowed = False

  @property
  def pineapple_allowed(self):
    return self._pineapple_allowed

  @pineapple_allowed.setter
  def pineapple_allowed(self, value):
    if value:
      password = input("Enter the password: ")
      if password == "Sw0rdf1sh!":
        self._pineapple_allowed = value
      else:
        raise ValueError("Alert! Intruder!")

pizza = Pizza(["cheese", "tomato"])
print(pizza.pineapple_allowed)
pizza.pineapple_allowed = True
print(pizza.pineapple_allowed)

False
Enter the password to permit pineapple: Sw0rdf1sh!
True

在这里插入图片描述
下一步是使用类来表示游戏对象

class GameObject:
  class_name = ""
  desc = ""
  objects = {}

  def __init__(self, name):
    self.name = name
    GameObject.objects[self.class_name] = self

  def get_desc(self):
    return self.class_name + "\n" + self.desc

class Goblin(GameObject):
  class_name = "goblin"
  desc = "A foul creature"

goblin = Goblin("Gobbly")

def examine(noun):
  if noun in GameObject.objects:
    return GameObject.objects[noun].get_desc()
  else:
    return "There is no {} here.".format(noun)

创建了一个 Goblin 类,它继承自 GameObjects 类。

我们还创建了一个新的函数 examine,它返回对象的描述。

现在我们可以添加一个新的 “examine” 动词到我们的字典

verb_dict = {
  "say": say,
  "examine": examine,
}
: say Hello!
You said "Hello!"

: examine goblin
goblin
A foul creature

: examine elf
There is no elf here.
:

在这里插入图片描述
这段代码补充了 Goblin 类 更多的细节

class Goblin(GameObject):
  def __init__(self, name):
    self.class_name = "goblin"
    self.health = 3
    self._desc = " A foul creature"
    super().__init__(name)

  @property
  def desc(self):
    if self.health >=3:
      return self._desc
    elif self.health == 2:
      health_line = "It has a wound on its knee."
    elif self.health == 1:
      health_line = "Its left arm has been cut off!"
    elif self.health <= 0:
      health_line = "It is dead."
    return self._desc + "\n" + health_line

  @desc.setter
  def desc(self, value):
    self._desc = value

def hit(noun):
  if noun in GameObject.objects:
    thing = GameObject.objects[noun]
    if type(thing) == Goblin:
      thing.health = thing.health - 1
      if thing.health <= 0:
        msg = "You killed the goblin!"
      else: 
        msg = "You hit the {}".format(thing.class_name)
  else:
    msg ="There is no {} here.".format(noun) 
  return msg
: hit goblin
You hit the goblin

: examine goblin
goblin
 A foul creature
It has a wound on its knee.

: hit goblin
You hit the goblin

: hit goblin
You killed the goblin!

: examine goblin
A goblin

goblin
 A foul creature
It is dead.
:

这只是一个简单的例子。
你可以创造不同的类(例如精灵,兽人,人类),与他们作战,使他们相互对抗,等等
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值