• 第037讲:类和对象:面向对象编程


    课堂小笔记

    面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

    self是什么?

    Python的self就相当于C++的this指针。类是图纸,对象是实例化的东西。由同一个类可以生成无数个对象,这些对象长得都很相似,都是来源于同一个类的属性和方法,当一个对象的方法被调用的时候,对象会将自身作为第一个参数传给这个self参数,接收到这个self的时候Python就知道你是哪一个对象在调用方法了。

    为什么都是调用kick方法,为啥结果不一样。因为在调用的时候,a.kick()的这里他有第一个参数,这个参数是隐藏的,就是把a这个对象的标志传进去;那setName()这里的self就接收到了,self.name就会去找到这个a对象的name属性,然后kick把他赋值打印出来。是Python默默在工作的。

    (类里面没有定义的属性都可以赋值,因为类是共有属性,对象可以有自己的私有属性)

    你听说过Python的魔法方法吗?

    被双下划线包围。

    __init__(self),称为构造方法,魔力体现在:只要在实例化一个对象的时候,那么这个方法就会在对象被创建的时候自动调用。(传说中的构成函数?)

    共有和私有

    苍井空是世界的,老婆是自己的!

    name mangling 名字改编,名字重整。在Python中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或者变量就变为私有的了。

    可以通过p._Person__name

    是伪私有。

    课后测试题及答案

    测试题:

    0. 以下代码体现了面向对象编程的什么特征?

    >>> "FishC.com".count('o')
    1
    >>> [1, 1, 2, 3, 5, 8].count(1)
    2
    >>> (0, 2, 4, 8, 12, 18).count(1)
    0

    都可以调用同一个方法,但是结果不一定相同。

     答:体现了面向对象编程的多态特征。

    1. 当程序员不想把同一段代码写几次,他们发明了函数解决了这种情况。当程序员已经有了一个类,而又想建立一个非常相近的新类,他们会怎么做呢?

    非常相近的新类,可以用继承吧,继承原先的类,再自己修改

    答:他们会定义一个新类继承已有的这个类,这样子就只需要简单添加重写需要的方法即可。例如已有龟类,那么如果要新定义一个甲鱼类,我们只需要让甲鱼类继承已有的龟类,然后重写壳的属性为“软的”即可(据说甲鱼的壳是软的)。

    2. self参数的作用是什么?

    用来接收对象的,可以让Python知道是哪个对象在使用方法和属性

    答:绑定方法,据说有了这个参数,Python 再也不会傻傻分不清是哪个对象在调用方法了,你可以认为方法中的 self 其实就是实例对象的唯一标志

    3. 如果我们不希望对象的属性或方法被外部直接引用,我们可以怎么做?

    可以使用“__”双下划线来使其私有化,虽然是伪私有,因为还是可以通过_类名__方法/属性 来引用。

    答:我们可以在属性或方法名字前边加上双下划线,这样子从外部是无法直接访问到,会显示AttributeError错误。

    >>> class Person:
    __name = '小甲鱼'
            def getName(self):
                    return self.__name
    
    >>> p = Person()
    >>> p.__name
    Traceback (most recent call last):
      File "<pyshell#56>", line 1, in <module>
        p.__name
    AttributeError: 'Person' object has no attribute '__name'
    >>> p.getName()
    '小甲鱼'

    我们把getName方法称之为“访问器”。Python事实上是采用一种叫“name mangling”技术,将以双下划线开头的变量名巧妙的改了个名字而已,我们仍然可以在外部通过“_类名__变量名”的方式访问:

    >>> p._Person__name'小甲鱼'

    当然我们并不提倡这种抬杠较真粗暴不文明的访问形式……!

    4. 类在实例化后哪个方法会被自动调用?

    __init__()

    答:__init__方法会在类实例化时被自动调用,我们称之为魔法方法。你可以重写这个方法,为对象定制初始化方案

    5. 请解释下边代码错误的原因:

    class MyClass:
            name = 'FishC'
            def myFun(self):
                    print("Hello FishC!")
                    
    >>> MyClass.name
    'FishC'
    >>> MyClass.myFun()
    Traceback (most recent call last):
      File "<pyshell#6>", line 1, in <module>
        MyClass.myFun()
    TypeError: myFun() missing 1 required positional argument: 'self'
    >>>

    没有一个对象传给self参数,

    答:首先你要明白类、类对象、实例对象是三个不同的名词

    我们常说的类指的是类定义,由于“Python无处不对象”,所以当类定义完之后,自然就是类对象。在这个时候,你可以对类的属性(变量)进行直接访问(MyClass.name)。

    一个类可以实例化出无数的对象(实例对象),Python 为了区分是哪个实例对象调用了方法,于是要求方法必须绑定(通过 self 参数)才能调用。而未实例化的类对象直接调用方法,因为缺少 self 参数,所以就会报错。

    动动手

    0. 按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。

    平日票价100元

    周末票价为平日的120%

    儿童半票

     答案:

    class Ticket():
            def __init__(self, weekend=False, child=False):
                    self.exp = 100
                    if weekend:
                            self.inc = 1.2
                    else:
                            self.inc = 1
                    if child:
                            self.discount = 0.5
                    else:
                            self.discount = 1
            def calcPrice(self, num):
                    return self.exp * self.inc * self.discount * num
    
    >>> adult = Ticket()
    >>> child = Ticket(child=True)
    >>> print("2个成人 + 1个小孩平日票价为:%.2f" % (adult.calcPrice(2) + child.calcPrice(1)))
    2个成人 + 1个小孩平日票价为:250.00

    1. 游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的^_^)

     1 import random as r
     2 
     3 legal_x = [0, 10]
     4 legal_y = [0, 10]
     5 
     6 class Turtle:
     7     def __init__(self):
     8         # 初始体力
     9         self.power = 100
    10         # 初始位置随机
    11         self.x = r.randint(legal_x[0], legal_x[1])
    12         self.y = r.randint(legal_y[0], legal_y[1])
    13 
    14     def move(self):
    15         # 随机计算方向并移动到新的位置(x, y)
    16         new_x = self.x + r.choice([1, 2, -1, -2])
    17         new_y = self.y + r.choice([1, 2, -1, -2])
    18         # 检查移动后是否超出场景x轴边界
    19         if new_x < legal_x[0]:
    20             self.x = legal_x[0] - (new_x - legal_x[0])
    21         elif new_x > legal_x[1]:
    22             self.x = legal_x[1] - (new_x - legal_x[1])
    23         else:
    24             self.x = new_x
    25         # 检查移动后是否超出场景y轴边界
    26         if new_y < legal_y[0]:
    27             self.y = legal_y[0] - (new_y - legal_y[0])
    28         elif new_y > legal_y[1]:
    29             self.y = legal_y[1] - (new_y - legal_y[1])
    30         else:
    31             self.y = new_y        
    32         # 体力消耗
    33         self.power -= 1
    34         # 返回移动后的新位置
    35         return (self.x, self.y)
    36 
    37     def eat(self):
    38         self.power += 20
    39         if self.power > 100:
    40             self.power = 100
    41 
    42 class Fish:
    43     def __init__(self):
    44         self.x = r.randint(legal_x[0], legal_x[1])
    45         self.y = r.randint(legal_y[0], legal_y[1])
    46         
    47     def move(self):
    48         # 随机计算方向并移动到新的位置(x, y)
    49         new_x = self.x + r.choice([1, -1])
    50         new_y = self.y + r.choice([1, -1])
    51         # 检查移动后是否超出场景x轴边界
    52         if new_x < legal_x[0]:
    53             self.x = legal_x[0] - (new_x - legal_x[0])
    54         elif new_x > legal_x[1]:
    55             self.x = legal_x[1] - (new_x - legal_x[1])
    56         else:
    57             self.x = new_x
    58         # 检查移动后是否超出场景y轴边界
    59         if new_y < legal_y[0]:
    60             self.y = legal_y[0] - (new_y - legal_y[0])
    61         elif new_y > legal_y[1]:
    62             self.y = legal_y[1] - (new_y - legal_y[1])
    63         else:
    64             self.y = new_y
    65         # 返回移动后的新位置
    66         return (self.x, self.y)
    67 
    68 turtle = Turtle()
    69 fish = []
    70 for i in range(10):
    71     new_fish = Fish()
    72     fish.append(new_fish)
    73 
    74 while True:
    75     if not len(fish):
    76         print("鱼儿都吃完了,游戏结束!")
    77         break
    78     if not turtle.power:
    79         print("乌龟体力耗尽,挂掉了!")
    80         break
    81 
    82     pos = turtle.move()
    83     # 在迭代器中删除列表元素是非常危险的,经常会出现意想不到的问题,因为迭代器是直接引用列表的数据进行引用
    84     # 这里我们把列表拷贝给迭代器,然后对原列表进行删除操作就不会有问题了^_^
    85     for each_fish in fish[:]:
    86         if each_fish.move() == pos:
    87             # 鱼儿被吃掉了
    88             turtle.eat()
    89             fish.remove(each_fish)
    90             print("有一条鱼儿被吃掉了...")

    作者:Agiroy_70

    本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。

    博主的文章主要是记录一些学习笔记、作业等。文章来源也已表明,由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。

    博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的文章,请原谅博主成为一个无耻的文档搬运工!

  • 相关阅读:
    关于HTTP以及TCP
    .NetCore表单提交文件
    C# Out变量
    .NET Core 网络数据采集 -- 使用AngleSharp做html解析
    C# 根据Url下载文件/获取文件流
    C# 模拟表单提交
    C# 获取Url路径的参数信息
    C# 采集页面数据
    .net core 3.1 设置可跨域
    C# json字符串转化成Dictionary
  • 原文地址:https://www.cnblogs.com/hale547/p/13335080.html
Copyright © 2020-2023  润新知