自学内容网 自学内容网

16. 面向对象编程

一、什么是面向对象

  对象(Object)是内存中专门用来存储数据的一块区域。对象中可以存放各种数据,比如:数字、布尔值、代码等。对象由 对象的标识(id)、对象的类型(type)和 对象的值(value)三部分组成。

  Python 是一门面向对象的编程语言。所谓的 面向对象 的语言,简单理解就是语言中所有操作都是通过对象来进行的。面向对象的编程语言,关注的是对象,而不关注过程。它将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。如果要使用某个功能,直接找到对应的对象即可。对于面向对象的语言来说,一切都是对象。面向对象 为单位,每种事物都具备自己的 属性方法/功能。面向对象这种方式编写的代码,可读性比较高,并且易于维护,可复用性比较高。但是这种方式,不太符合常规的思维,编写起来稍微麻烦一点。

  与之对应的就是 面向过程 的编程语言,面向过程的编程语言指将我们的程序的逻辑分解为一个一个的步骤,通过对每个步骤的抽象,来完成程序。但是这种编写的代码往往只适用于一个功能。如果要实现别的功能,即使功能相差极小,也往往需要重新编写代码,所以它的可复用性比较低,并且难以维护。面向过程 强调的是 功能行为,以 函数 为最小单位,考虑怎么做;这种编程方式,符合我们人类思维,编写起来相对简单。

  面向过程其实是最为实际的一种思考方式,就算是面向对象的方法也是含有面向过程的思想。可以说面向过程是一种基础的方法。它考虑的是实际地执行。一般的面向过程是从上往下步步求精。面向对象主要是把事物给对象化,对象包括属性与行为。当程序规模不是很大的时候,面向过程的方法还会体现出一种优势。因为程序的流程很清楚,按着模块与函数的方法可以很好的组织。但对于复杂而庞大的系统来说,面向过程显着就很无力了。

二、类与对象

  (Class)和 对象(Object)是面向对象的核心概念。 是对一类事物的描述,是 抽象的、概念上的定义,简单理解就相当于一个图纸;对象 是实际存在的该类事物的每个个体,是 具体的,因此也称为 实例(instance);在程序中我们根据类来创建对象。我们也称对象是类的实例。如果多个对象是通过一个类创建的,我们称这些对象是一类对象。

  面向对象程序设计的重点是 类的设计,类的设计,其实就是 类的成员的设计

  1. 创建类,设计类的内部成员(属性、方法)
  2. 创建类的对象
  3. 通过对象,调用其内部声明的属性或方法,完成相关的功能
class Person():
    pass

print(Person)
print(id(Person))
print(type(Person), '\n')

# 使用类创建对象
p1 = Person()
print(p1)
print(type(p1),'\n')

p2 = Person()
p2.name = "Sakura"
print(p2.name,'\n')


name = str("Sakura")
# 我们可以用isinstance()用来检查一个对象是否是一类类的实例
print(isinstance(p1, Person))
print(isinstance(p2, Person))
print(isinstance(name, Person))

类也是一个对象,类是创建对象的对象;

类是 type 类型的对象,定义类实际上就是定义了一个 type 类型的对象;

三、类的成员

3.1、属性

  属性 用来描述具体某个对象的特征。描述的是对象的状态信息,通常以变量的形式进行定义。在类中我们定义的变量,将会成为所有实例的公共属性,所有实例都可以通过 对象.属性名 的方式访问这些变量。

  直接在类中定义的属性是 类属性,类属性可以通过类或类的实例访问到,但是类属性只能通过类对象来修改,无法通过实例对象修改。我们还可以创建的实例中动态的添加属性,这种属性属于 实例属性。实例属性只能通过实例对象来访问和修改,类对象无法访问修改。

  当我们调用一个对象时,解析器会先在当前对象中寻找是否含有该属性,如果有则直接返回当前对象的属性值,如果没有则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,如果没有则报错。

class Person:
    # 类属性,直接在类中定义的属性
    name = "Unknown"

# 类属性可以通过类或类的实例访问到
p1 = Person()
print("Person: ", Person.name)
print("p1", p1.name, '\n')

# 类属性只能通过类对象来修改,无法通过实例对象修改
Person.name = "Sakura"
print("Person: ", Person.name)
print("p1", p1.name, '\n')

# 通过实例对象添加的属性属于实例属性
# 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
p2 = Person()
p2.name = "Mikoto"
print("Person: ", Person.name)
print("p2: ", p2.name, '\n')

del p2.name
print(p2.name)

  类对象和实例对象都可以保存属性,如果这个属性是所有的实例共享的,则应该将其保存到类对象中。如果这个属性是某个实例独有的,则应该保存到实例对象中。一般情况下,属性保存在实例对象中。

class Person:
    # 类属性,直接在类中定义的属性
    name = "Unknown"

p1 = Person()
p2 = Person()

print(id(Person.name))
print(id(p1.name))
print(id(p2.name))

3.2、方法

  方法 是类或对象行为特征的抽象,用来完成某个功能的操作。在其它编程语言中,方法 也被称为 函数过程。在类中定义的函数,将会成为所有实例的公共方法,所有该类实例都可以通过 对象.方法名() 的形式调用方法。在实际开发中,我们可以将重复的代码、具有独立功能的代码抽取到方法中。使用方法后,我们可以提高代码的复用性和可维护性。

3.2.1、实例方法

  实例方法 每次调用时,解析器都会自动传递第一个实参,这个参数就是调用方法的 对象本身,一般我们都会将这个参数命名为 self。实例方法通过对象调用时,会自动将当前对象作为 self 传入,实例方法通过类调用时,不会自动传递 self,必须我们手动传递 self。

class Person:

    # 实例方法每次调用时,解析器都会自动传递第一个实参
    # 实例方法的第一个参数就是调用方法的对象本身,一般我们都会将这个参数命名为self
    def say_hello(self):
        print(self)
        # 在方法中不能直接访问实例对象的属性
        print("你好,我是 %s" %self.name)

p1 = Person()
print(p1,'\n')

p1.name = "Sakura"
# 实例方法通过类调用时,会自动将当前对象作为self传入
p1.say_hello()
# 实例方法通过类调用时,不会自动传递self,必须我们手动传递self
Person.say_hello(p1)
print()

p2 = Person()
print(p2,'\n')

p2.name = "Mikoto"
p2.say_hello()

注意:方法调用时,解析器会把当前对象当作第一个参数自动传递,所以定义实例方法时,至少定义一个形参;

3.2.2、类方法

  在类内部使用 @classmethod 来修饰的方法属于 类方法, 类方法的第一个参数就是当前的 类对象,一般我们都会将这个参数命名为 cls。类方法可以通过类调用,也可以通过实例调用。

class Person:
    # 类属性
    count = 0
  
    # 在类内部使用@classmethod来修饰的方法属于类方法
    # 类方法的第一个参数就是当前的类对象,一般我们都会将这个参数命名为cls
    @classmethod
    def showInfo(cls):
        print(cls)
        print("我的身份编号为:",cls.count)

p1 = Person()
# 类方法可以通过类调用
Person.showInfo()
# 类方法也可以通过实例调用
p1.showInfo()

注意:方法调用时,解析器会把当前类对象当作第一个参数自动传递,所以定义实例方法时,至少定义一个形参;

3.2.3、静态方法

  在类中使用 @staticmethod 来修饰的方法属于 静态方法,静态方法不需要指定任何的默认参数。静态方法,基本上是一个和方法无关的方法,它只是保存到当前类中的函数,静态方法一般都是一些工具方法,和当前类无关。

class Person:

    # 在类中使用@staticmethod来修饰的方法属于静态方法
    # 静态方法不需要指定任何的默认参数
    @staticmethod
    def show():
        print("我是一个人")

p1 = Person()

# 静态方法可以通过类或实例取调用
Person.show()
p1.show()

3.3、魔法属性

__doc__             # 查看类的描述信息
__module__          # 查看当前操作的对象在哪个模块
__class__           # 表示当前操作的对象的类是什么
__dict__            # 类或对象中的所有属性
class Person:
    """人的描述类"""
    name = "unknown"

    def show_info(self):
        print(f"我是一个人,名字是: {self.name}")

p = Person()
p.name = "Sakura"
p.age = 10

print(f"p.doc: {p.__doc__}")
print(f"p.module: {p.__module__}")
print(f"p.class: {p.__class__}")
print(f"Person.dict: {Person.__dict__}")
print(f"p.dict: {p.__dict__}")

Python 解释器会将对象(类对象、实例对象)中的所有属性、方法,统统用字典存储,将属性的名字变为字典的 key,将属性对应的数据当做字典中的 value。将方法的名字变为字典中的 key,将方法所对应的引用当做字典中的 value;

3.4、魔法方法

  在类中可以定义一些特殊方法(魔法方法),特殊方法都是以双下划线 __ 开头,双下划线 __ 结尾。特殊方法不需要我们自己调用,它会在特殊的时候自动调用。

  __init__() 会在创建对象后立即执行,它可以用来向新创建的对象初始化属性。

object.__init__(self[, ...])
class Person:

    # __init__(self)会在对象创建后立即执行
    # __init__(self)可以用来向新创建的对象初始化属性
    def __init__(self, name, age):
        # 通过self向新创建堆额对象中初始化属性
        self.name = name
        self.age = age
  
    def showInfo(self):
        # 在方法中不能直接访问实例对象的属性
        print("你好,我是 %s,我今年 %d 岁。" %(self.name,self.age))

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p1.showInfo()

p2 = Person("Mikoto",14)
p2.showInfo()

__init()__ 方法必须返回 None;

  __new__() 这个特殊方法会在创建类时自动调用。

object.__new__(cls[, ...])
class Person:

    # __new__(cls)这个特殊方法会在创建类时自动调用
    def __new__(cls):
        print("__new__(cls)执行了")
        print(cls)

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person()

  __del__() 这个特殊方法会在对象删除前调用。

object.__del__(self)
class Person:
    # __init__(self)会在对象创建后立即执行
    # __init__(self)可以用来向新创建的对象初始化属性
    def __init__(self, name, age):
        # 通过self向新创建堆额对象中初始化属性
        self.name = name
        self.age = age

    # __del__(self)会在对象删除前调用
    def __del__(self):
        print("del(self)方法执行了")
        print(self.name,"被删除了")

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p1 = None

p2 = Person("Mikoto",14)

  __call__() 会在对象加括号后自动执行。

object.__call__(self[, args...])
class Person:
  
    def __call__(self):
        print("call方法被执行了")

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person()
p1()

  __str__() 会在当前对象转换为字符串时使用,它可以用来指定对象转换为字符串的结果。

object.__str__(self)
class Person:
    # __init__(self)会在对象创建后立即执行
    # __init__(self)可以用来向新创建的对象初始化属性
    def __init__(self, name, age):
        # 通过self向新创建堆额对象中初始化属性
        self.name = name
        self.age = age

    # __str__(self)会在当前对象转换为字符串时使用
    # 它可以用来指定对象转换为字符串的结果
    def __str__(self):
        return "Person[name: %s, age %d]"%(self.name,self.age)

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
print(p1)

  __repr__() 这个特殊方法会在当前对象使用 repr() 函数时调用,它的作用对象在‘交互模式’中直接输出的效果。

object.__repr__(self)
class Person:
    # __init__(self)会在对象创建后立即执行
    # __init__(self)可以用来向新创建的对象初始化属性
    def __init__(self, name, age):
        # 通过self向新创建堆额对象中初始化属性
        self.name = name
        self.age = age

    # __repr__(self)这个特殊方法会在当前对象使用repr()函数时调用
    # 它的作用对象在‘交互模式’中直接输出的效果
    def __repr__(self):
        return "Person[name: %s, age %d]"%(self.name,self.age)

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
print(p1)

  __getattribute__() 方法可以实现属性拦截的作用。

object.__getattribute__(self, name)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __getattribute__(self, attribute):
        if attribute == "age":
            return "直接问别人年龄是不礼貌的哦"
        else:
            return object.__getattribute__(self, attribute)

p = Person("Sakura", 10)
print(f"name: {p.name}")
print(f"age: {p.age}")

  如果我们想像操作字典的方式用键值对的方式操作对象,可以使用如下方法:

object.__getitem__(self, key)           # 用键值对的方式获取属性
object.__setitem__(self, key, value)    # 用键值对的方式设置属性
object.__delitem__(self, key)           # 用键值对的方式删除属性
class Person:

    def __getitem__(self, key):
        print("__getitem()__方法执行了")

    def __setitem__(self, key, value):
        print("__setitem()__方法执行了")

    def __delitem__(self, key):
        print("__delitem()__方法执行了")

p = Person()
p["name"] = "Sakura"
name = p["name"]
del p["name"]

  有关比较的特殊方法。

object.__lt__(self, other)  # 会在对象做小于(<)时调用,该方法的返回值会作为比较结果
object.__le__(self, other)  # 会在对象做小于等于(<=)时调用,该方法的返回值会作为比较结果
object.__eq__(self, other)  # 会在对象做等于(==)时调用,该方法的返回值会作为比较结果
object.__ne__(self, other)  # 会在对象做不等于(!=)时调用,该方法的返回值会作为比较结果
object.__gt__(self, other)  # 会在对象做大于(>)时调用,该方法的返回值会作为比较结果
object.__ge__(self, other)  # 会在对象做大于等于(>=)时调用,该方法的返回值会作为比较结果
class Person:
    # __init__(self)会在对象创建后立即执行
    # __init__(self)可以用来向新创建的对象初始化属性
    def __init__(self, name, age):
        # 通过self向新创建堆额对象中初始化属性
        self.name = name
        self.age = age

    # __gt__(self,other)会在对象做小于时调用,该方法的返回值会作为比较结果
    # 它需要两个参数,self表示当前对象,other表示和当前对象比较的对象
    def __gt__(self,other):
        return self.age > other.age

# 调用类创建对象是,类后面的所有参数都会一次传递给__init__(self)中
p1 = Person("Sakura",10)
p2 = Person("Mikoto",15)
print(p1 > p2)
print(p2 > p1)

  有关运算的特殊方法:

object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(self, other)
object.__matmul__(self, other)
object.__truediv__(self, other)
object.__floordiv__(self, other)
object.__mod__(self, other)
object.__divmod__(self, other)
object.__pow__(self, other[, modulo])
object.__lshift__(self, other)
object.__rshift__(self, other)
object.__and__(self, other)
object.__xor__(self, other)
object.__or__(self, other)
object.__len__(self)        # 获取对象的长度
object.__bool__(self)       # 将当前对象个转换为布尔值

3.5、对象创建的流程

  p1 = Person() 的运行流程:

  1. 创建一个变量;
  2. 在内存中创建一个空对象;
  3. 自动调用类中 __init__(self) 方法,然后将空对象已经调用类是括号内传入的的参数一同传给 __init__() 方法;
  4. 将对象的 id 赋值给变量;
class Person:
    # 类代码块中的代码只在类定义的时候执行一次
    print("我是Person中的代码块")

    def __init__(self):
        print("我是",self,"__init__(self)方法")

p1 = Person()
p2 = Person()

类代码块中的代码只在类定义的时候执行一次;


原文地址:https://blog.csdn.net/flurry_heart/article/details/144316570

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!