python入门08——面向对象


Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。

面向对象技术简介

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 方法: 类中定义的函数。 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量: 定义在方法中的变量,只作用于当前实例的类。
  • 实例变量: 在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • 继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • 实例化: 创建一个类的实例,类的具体对象。 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。

Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。

对象可以包含任意数量和类型的数据。

一、类和对象

1.1 类

是对一群具有 相同特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用。
特征被称为属性, 行为被称为方法

类就相当于制造飞机时的图纸,是一个模板,是负责创建对象的。其语法格式如下所示:

class 类名:

    def 方法1(self, 参数列表):
        pass
    
    def 方法2(self, 参数列表):
        pass

一个简单的实例:

class MyClass:
    """一个简单的类实例"""
    i = 12345

    def f(self):
        return 'hello world'
1.2 对象

类实例化后,可以使用其属性。实际上,创建一个类之后,可以通过类名访问其属性。

对象是由类创建出来的一个具体存在,可以直接使用。
由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法。

类对象支持两种操作:属性引用和实例化。
属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name。
对象就相当于用图纸制造的飞机。其具体使用如下实例:

# 实例化类
x = MyClass()

# 访问类的属性和方法
print("MyClass 类的属性 i 为 :", x.i)  # 属性引用为obj.name
print("MyClass 类的方法 f 输出为:", x.f())
# MyClass 类的属性 i 为: 12345
# MyClass 类的方法 f 输出为: hello world

在程序开发中,应该先有类,再有对象

1.3 类的设计

在使用面向对象开发前,应该首先分析需求,确定程序中需要包含哪些类。
在程序开发中,要设计一个类,通常需要满足一下三个要素:

类名:这类事物的名字,满足大驼峰命名法
属性:这类事物具有什么样的特征
方法:这类事物具有什么样的行为

在这里插入图片描述
注意:需求中没有涉及的属性或者方法在设计类时,不需要考虑。

二、面向对象

2.1 封装、继承、多态
2.1.1 封装

面向对象(Object-Oriented)有三个基本特征,分别为:封装、继承、多态

封装: 封装就是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体。

对象是封装类的实例。封装其实就是一种信息隐蔽技术。

# 一个简单的列表例子
list1 = [2, 1, 7, 5, 3]
list1.sort()  # 我们不知道sort具体的实现细节,但是我们可以直接使用
list1.append(9)
print(list1)
# 结果为:[1, 2, 3, 5, 7, 9]
2.1.2 继承

继承: 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

定义一个MyList类继承list类,那么由MyList类创建的对象便可以使用list类的方法。

class MyList(list):
    pass  # 占位符:代表此时的类不做任何事情,只是继承list类

list1 = MyList()
list1.append(3)
list1.append(4)
print(list1)
# 结果为:[3, 4]

子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法。如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性。
一个继承的实例:

import random

class Fish:
    def __init__(self):  # 记录此时鱼儿的位置x,y
        self.x = random.randint(0, 10)  # 初始时随机产生位置坐标
        self.y = random.randint(0, 10)

    def move(self):
        self.x -= 1  # 每次向左游一步
        print("我的位置是:", self.x, self.y)

class GoldFish(Fish):
    pass

class Shark(Fish):
    def __init__(self):  # 重写了__init__方法
       self.hungry = True

    def eat(self):
        if self.hungry:
            print("eating....")
            self.hungry = False
        else:
            print("not eating....")

fish = Fish()  # 父类对象
fish.move()
fish.move()  # 可以正确移动
gf = GoldFish()  # 子类对象
gf.move()
gf.move()  # 可以正确移动
shark = Shark()  # 子类对象
shark.move()  # 报错

该实例会出现AttributeError: 'Shark' object has no attribute 'x'错误,是因为shark子类中重写了父类中的__init__方法,在子类中找不到move中x的值。
【解决方案】用super().在子类中调用父类的__init__方法。

class Shark(Fish):
    def __init__(self):
        super().__init__()  # super后有括号!!
        # 用Fish.__init__(self)也可以实现,但此时调用该方法的类不一定是Fish的子类
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("eating....")
            self.hungry = False
        else:
            print("not eating....")

多继承: class DerivedClassName(Base1, Base2, Base3),使用需小心!

2.1.3 多态

多态: 多态同一个行为具有多个不同表现形式或形态的能力。是指一个类实例(对象)的相同方法在不同情形有不同表现形式。

类的多态要满足两个前提条件:①继承:多态一定是发生在子类和父类之间;②重写:子类重写了父类的方法。以下是一个简单的实例:

class CLanguage:
    def say(self):  # 重写了父类的say方法
        print("调用的是 Clanguage 类的say方法")

class CPython(CLanguage):
    def say(self):  # 重写了父类的say方法
        print("调用的是 CPython 类的say方法")

class CLinux(CLanguage):
    def say(self):  # 重写了父类的say方法
        print("调用的是 CLinux 类的say方法")
        
a = CLanguage()
a.say()

a = CPython()
a.say()

a = CLinux()
a.say()

由上述代码可见,CPython 和 CLinux 都继承自 CLanguage 类,且各自都重写了父类的 say() 方法。从运行结果可以看出,同一变量 a 在执行同一个 say() 方法时,由于 a 实际表示不同的类实例对象,因此 a.say() 调用的并不是同一个类中的 say() 方法,这就是多态。

Python 在多态的基础上,衍生出了一种更灵活的编程机制,称为“鸭子模型”或“鸭子类型”。

class WhoSay:
    def say(self, who):
        who.say()
class CLanguage:
    def say(self):
        print("调用的是 Clanguage 类的say方法")

class CPython(CLanguage):
    def say(self):
        print("调用的是 CPython 类的say方法")

class CLinux(CLanguage):
    def say(self):
        print("调用的是 CLinux 类的say方法")
        
a = WhoSay()
#调用 CLanguage 类的 say() 方法
a.say(CLanguage())
#调用 CPython 类的 say() 方法
a.say(CPython())
#调用 CLinux 类的 say() 方法
a.say(CLinux())

此程序中,通过给 WhoSay 类中的 say() 函数添加一个 who 参数,其内部利用传入的 who 调用 say() 方法。这意味着,当调用 WhoSay 类中的 say() 方法时,我们传给 who 参数的是哪个类的实例对象,它就会调用那个类中的 say() 方法。
此处参考文章@http://c.biancheng.net/view/5833.html

2.2 self参数是什么?

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

class Test:
    def prt(self):
        print(self)  # 输出当前self的值
        print(self.__class__)  # 输出当前self指向的类
 
t = Test()  # t是Test类创建的实例对象
t.prt()  # 调用Test类的prt方法

以上实例的执行结果为:
在这里插入图片描述
一个更为直观的例子:

class A:
    def fun(self):
        print("我是A类!" + "  此时的self值为:" + str(self) + ",它指向" + str(self.__class__))


class B:
    def fun(self):
        print("我是B类!" + "  此时的self值为:" + str(self) + ",它指向" + str(self.__class__))


a = A()  # A类对象
b = B()  # B类对象
# 两个对象调用不同类中的同名函数fun(),输出的结果不一样
a.fun()
b.fun()

由此可见,即使两个对象都调用不同类中的同名函数fun(),输出的结果仍是不一样的,因为fun方法被调用时self的值不同,它指向的类也不同。其运行结果为:
在这里插入图片描述

2.3 类的内置方法

类中常用的特殊方法(构造方法)如下表所示:
在这里插入图片描述

2.3.1 __init__方法

__init__方法是专门用来定义一个类具有哪些属性的方法。

当使用 类名() 创建对象时,会自动执行以下操作:
    1.为对象在内存中分配空间 —— 创建对象
    2.为对象的属性设置初始值 —— 初始化方法(init)
这个 初始化方法 就是 __init__() 方法,是对象的内置方法。其结构如下所示:

def __init__(self):
    self.data = []

类定义了 __init__() 方法,类的实例化操作会自动调用 该方法。如下实例化类 MyClass,对应的 __init__()方法就会被调用:

x = MyClass()

__init__() 方法可以有参数,参数通过 __init__() 传递到类的实例化操作上。如下实例所示:

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart


x = Complex(3.0, -4.5)  # 这两个实参分别传到realpart和imagpart中去
print(x.r, x.i)  # 输出结果:3.0 -4.5

应用场景: 开发中,如果希望在创建对象的同时就设置对象的属性。我们可以把希望设置的属性值,定义成__init__ ()的参数,然后在方法内部使用 self.属性 = 形参 接收外部传递的参数。在创建对象时,使用 类名(属性1, 属性2...) 调用即可。

2.3.2 __del__方法

在 Python 中,当使用 类名() 创建对象时,为对象分配完空间后,会自动 调用 __init__ 方法。那么同样地,当一个对象从内存中被销毁前,会自动调用 __del__ 方法。

class Cat:

    def __init__(self, new_name):
        self.name = new_name
        print("%s 来了,此时调用的是__init__方法" % self.name)

    def __del__(self):
        print("%s 去了,,此时调用的是__del__方法" % self.name)


# tom 是一个全局变量
tom = Cat("Tom")
print(tom.name)  # 输出Tom

# del 关键字可以删除一个对象
del tom  

上述代码的执行结果为:
在这里插入图片描述
生命周期: 一个对象从调用 类名() 创建,生命周期开始一个对象的 __del__ 方法一旦被调用,生命周期结束。
应用场景:__del__ ()如果希望在对象被销毁前,再做一些事情,可以考虑使用 __del__() 方法。

2.4 公有和私有

在python中定义私有变量只需要在变量名或函数名前加上两个下划线“__”,那么这个变量或函数就变成私有的了,不能在类的外部调用,只能在类的内部调用。
下面是一个公有的变量调用:

# name公有
class Person:
    name = 'Zhai'

p = Person()
print(p.name)  # 输出Zhai

将公有变量name改为私有变量后:

# name私有
class Person:
    __name = 'Zhai'  # 将name改为私有

p = Person()
print(p.__name)

其运行结果会报错,因为私有变量是不能被直接调用的。如下所示:
在这里插入图片描述
那么我们该怎么获取私有变量的值呢,其实可以通过实例._类名__变量名来获取值。如下所示:

class Person:
    __name = 'Zhai'  # 将name改为私有

p = Person()
print(p._Person__name)  # 注意类名前面只有一个_
# 运行结果为Zhai

其实上述步骤内部逻辑与使用get方法一致,也就是下面所展示的代码。

class Person:
    __name = 'Zhai'
    def getName(self):
        return self.__name
p = Person()
# print(p.__name)  # 该语句会报错
print(p.getName())  # 变量私有情况下,取值必须调用get方法
# 运行结果为Zhai
2.5 类的组合

类的组合,即在类实例化时,将另一个类的实例作为参数传入,这样可以将两个实例关联起来。

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
【例】定义一个类,叫水池,水池里面要有乌龟和鱼,调用水池中的方法可显示水池中乌龟和鱼的数量。

class Turtle:
    def __init__(self, x):
        self.num = x


class Fish:
    def __init__(self, x):
        self.num = x


class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)
        self.fish = Fish(y)

    def print_num(self):
        print("水池里公有乌龟 %d 只,小鱼 %d 条!"% (self.turtle.num, self.fish.num))

pool = Pool(1, 10)
pool.print_num()
# 运行结果为:水池里公有乌龟 1 只,小鱼 10 条!

在该实例中,在Pool类中使用了Fish类以及Turtle类的实例作为参数。

2.6 绑定

Python严格要求方法需要实例时才能被调用,这种限制其实就是Python所谓的绑定概念。
如果方法括号内不带self,则可以直接用类对象即类名.方法名调用。

class Test:
    def print_Test():
        print("not correct!")

Test.print_Test()
# 运行结果为:not correct!

但是,一旦用该类的实例对象来调用类中方法就不可行了。

class Test:
    def print_Test():
        print("not correct!")

# Test.print_Test()
t = Test()  # 实例对象
t.print_Test()
# 报错:TypeError: print_Test() takes 0 positional arguments but 1 was given

如果方法括号内带了self,则不能直接用类对象调用,只能用实例对象调用。

class Test2:
    def setXY(self, x, y):
        self.x = x
        self.y = y
    def printXY(self):
        print(self.x, self.y)

tt = Test2()
print(tt.__dict__)  # 查看实例对象内部所有属性名和属性值组成的字典
print(Test2.__dict__)  # 查看类对象内部所有属性名和属性值组成的字典
tt.setXY(4, 5)  # 实例对象调用类中方法
print(tt.__dict__)  # 此时__dict__中便有了值
print(Test2.__dict__)  # 而类对象中没有,因为绑定使得这两个对象只属于实例对象

上述代码的运行结果为:
在这里插入图片描述

三、与类和对象相关的内置函数

3.1 issubclass(class, classinfo)

issubclass() 方法用于判断参数 class 是否是类型参数 classinfo 的子类。

class A:
    pass

class B(A):
    pass

print(issubclass(B, A))  # B是A的子类
# True
3.2 isinstance(object, classinfo)

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
参数:
    object – 实例对象。
    classinfo – 可以是直接或间接类名、基本类型或者由它们组成的元组。

class A:
    pass

class B(A):
    pass

b = B()
print(isinstance(b, B))  # b是类B的实例化对象
print(isinstance(b, A))  # B类继承A类
# 结果都为True
3.3 hasattr(object, name)

hasattr() 函数用于判断对象是否包含对应的属性。
参数:
    object – 对象。
    name – 字符串,属性名。

class C:
    def __init__(self, x=0):
        self.x = x

c = C()
print(hasattr(c, 'x'))  # 一定要加字符串!!
# True
3.4 getattr(object, name[, default])

getattr() 函数用于返回一个对象属性值。

class C:
    def __init__(self, x=0):
        self.x = x

c = C()
print(getattr(c, 'x'))  # 存在值输出值为0
print(getattr(c, 'y', "您所访问的属性不存在!"))  # 输出:您所访问的属性不存在!
3.5 property(fget[, fset[, fdel[, doc]]]])

property() 函数的作用是在新式类中返回属性值。
参数:
    fget – 获取属性值的函数
    fset – 设置属性值的函数
    fdel – 删除属性值函数
    doc – 属性描述信息

class Test:
    def __init__(self, size=10):
        self.size = size
    def getSize(self):
        return self.size
    def setSize(self, value):
        self.size = value
    def delSize(self):
        del self.size
    x = property(getSize, setSize, delSize)

t = Test()
print(t.x)  # 该步骤代表获取x的值,对应getSize方法
t.x = 18  # 该步骤代表设置x的值,对应setSize方法
print(t.x)
del t.x  # 该步骤代表删除x,对应delSize方法
print(t.x)

更多内置方法的使用请参考@https://www.runoob.com/python/python-built-in-functions.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值