面向对象

面向对象

3种常见的编程方式

  • 面向过程
  • 函数式编程
  • 面向对象

面向过程

所谓过程就是我们解决问题的步骤,一步步的按照流程走,有先后之分。

它呢,整个设计就好比流水线,思维上比较机械化。

优缺点:

  • 优点
    • 复杂的问题流程化,将问题分解简化。
  • 缺点
    • 拓展性不好

面向对象

核心是对象。

正式的来说

  • 对象是一个数据以及相关行为的集合
  • 面向对象是功能上指向建模对象

通过数据行为方式来描述交互对象的集合

在Python中,一切皆为对象。

面向对象的优缺点:

  • 优点
    • 解决程序的拓展性
  • 缺点
    • 就是复杂度远高于面向过程
    • 交互式解决问题,无法准确预测结果

在现实世界中,以我们为例

object1:
Tom
	特征:
    school=zucc
    name=tom
    age=21
    sex=male
    
	技能:
    eat
    study
    sleep
    
object2:
Jack
	特征:
    school=zucc
    name=jack
    age=20
    sex=male
    
	技能:
    eat
    study
    sleep
    sing

类就是类别、种类

对象就是特征和技能的统一体。

类这是这一系列相似对象的特征和技能的结合。

对于现实世界,先有个体(即对象),才有类别;但对于程序,必须先有类,然后才有对象的。

面向对象编程

OOP(object oriented programming)

就是一种程序设计思想,OOP把对象作为程序的一个基本单元。一个对象就包含了数据和操作数据的函数。

在Python中,所有数据类型都可以视为对象,同时,我们也可以自定义对象。

自定义的对象的数据类型就是面向对象中类(class)的概念。

Demo:

假如要处理我们的成绩。为了表示学生的成绩:

  • 面向过程的方式
stu1 = {"name":"Tom", "score":99}
stu2 = {"name":"Jack", "score":82}
  • 利用函数来实现
def find_score(stu):
    print(stu["name"], ":", stu["score"])
  • 面向对象
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def find_score(self):
        print(self.name, ":", self.score)

面向对象技术简介

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

类的定义和使用

面向对象设计的思想,先抽象出类,然后再根据类创建实例。

class ClassName(object):
    "dicstring"
    class_statement

类的命名,大驼峰式

所谓大驼峰就是变量名称的单词的首字母大写。

创建一个类

class MyFirstClass:
    pass

类的作用是模板。我们可以在创建实例的时候,把一些我们认为必须要绑定的属性填写进去。这时就通过特殊的__init__方法。在创建实例的时候,绑定相关的属性。比如前面的name,score。

class Student:
    school = "zucc"

    def __init__(self, name, score):
        self.name = name
        self.score = score


stu1 = Student("liyue", 99)  # 实例化
print((stu1.name, stu1.score, stu1.school))

和普通函数相比,在类中定义方法时,第一个参数必须是self。除第一个参数外,其他的和普通函数没有什么区别。

self 代表的是实例,而非类。

__init__方法

  • 1.为对象初始化自己独有的特征
  • 2.该方法中可以有任意的代码,但是一定不可以有返回值。

数据封装

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def find_score(self):
        print(self.name, ":", self.score)


stu1 = Student("Tom", 99)
stu1.find_score()
print(type(stu1))

我们通过__init__()让stu1实例本身就拥有了相关数据,如果要访问这些数据,我们可以直接在Student类的内部去定义相关的函数来访问数据,以此“封装”数据。

这些封装数据的函数和Student类本身是关联起来的,他们被称之为方法。

类的两个作用:

  • 属性引用

    • 类名.属性
  • 实例化

    • 类名加上一个括号就是实例化,他能够自动触发__init__函数的运行,进而为每个实例定制自己的特征。

类属性的补充

类属性的查看

  • 1.dir(类名)
    • 返回一个列表
  • 2.类名.__dict__
    • 返回一个字典,key为属性名,value为属性值。

特殊的类属性

类名.__name__  # 返回类的名称
类名.__doc__  # 类的文档字符串
类名.__base__  # 类的第一个父类
类名.__bases__  # 类的所有父类构成的元组
类名.__modul__  # 类定义所在的模块
类名.__class__  # 实例对应的类
类名.__dict__  # 返回一个字典,key为属性名,value为属性值

总结:

class ClassName:
    def __init__(self, para1, para2, ...):
        self.obj_attr = para1
        self.obj_sttr = para2
    def 方法名(self):
        pass
    def 方法名2(self):
        pass
obj = ClassName(para1, para2, ...)  
# 对象的实例化,代表一个具体的东西
# ClassName():调用__init__
# 括号内传参,无需传入self,参数一一对应
# 结果返回一个对象obj
obj.obj_attr  # 查看对象的属性
obj.方法名1  # 调用类的方法

对象之间的交互

假如说现在定义两个类,Person,Dog。

class Person:

    def __init__(self, name, aggressivity, life_value):
        self.name = name
        self.aggressivity = aggressivity
        self.life_value = life_value

    def attack(self, dog):
        dog.life_value -= self.aggressivity

class Dog:

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name
        self.breed = breed
        self.aggressivity = aggressivity
        self.life_value = life_value

    def bite(self, people):
        people.life_value -= self.aggressivity

per = Person("Jack", 10, 1000)
dog = Dog("jeorry", "Husky", 8, 1000)

while True:
    per.attack(dog)
    if dog.life_value <= 0:
        print(per.life_value)
        break
    dog.bite(per)
    if per.life_value <= 0:
        print(dog.life_value)
        break

类命名空间与对象、实例的空间

创建一个类就会创建一个类的名称空间,用来存储我们定义的所有的变量名。这些名字就是属性。

类的属性有两种:

  • 静态属性
    • 直接在类中定义的变量
  • 动态属性
    • 在类中定义的方法

静态属性是共享给所有对象的

动态属性是绑定到所有对象的

class Student:
    school = 'zucc'
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def find_score(self):
        print(self.name, ":", self.score)


stu1 = Student("Tom", 99)
stu2 = Student("Jack", 89)

print(id(stu1.school))
print(id(stu2.school))

print(stu1.find_score)
print(stu2.find_score)
print(Student.find_score)

#2957132795104
#2957132795104
#<bound method Student.find_score of <__main__.Student object at 0x000002B089C853C8>>
#<bound method Student.find_score of <__main__.Student object at 0x000002B089C85978>>
#<function Student.find_score at 0x000002B089CE81E0>

函数的三大特性

  • 继承
  • 多态
  • 封装

继承

在面向对象编程中,当我们定义一个新类的时候,可以从某个现有的类继承,新的类就被称为子类(SubClass),而被继承的类则被称为基类、父类、超类(Base Class、Father Class、Super Class)

比如,我们定义一个动物类(Animal),其有一个run()方法如下:

class Animal(object):
    def run(self):
        print("Animai is running.")

        
class Animal2(object):
    pass


class Dog(Animal):  # Dog类继承自Animal类
    pass


class Husky(Animal, Animal2):  # 多继承,用逗号分隔开
    pass


dog = Dog()  # Dog类创建实例
dog.run()  # Dog类内未定义方法,实例直接调用父类的方法
print(Husky.__bases__)  # 继承的查看

#Animai is running.
#(<class '__main__.Animal'>, <class '__main__.Animal2'>)

继承的查看

ClassName.__bases__

如果不指定基类,Python类会默认继承object类,object是所有Python类的基类,提供一些常见方法的实现。

多态

当子类和父类存在相同的方法时,子类的方法会覆盖父类的方法,在运行代码时,总会调用子类和父类同名的方法。

这样,就是继承的另一个好处,多态。

class Animal(object):
    def run(self):
        print("Animai is running.")


class Animal2(object):
    pass


class Dog(Animal):  # Dog类继承自Animal类
    def run(self):
        print("Dog is running.")


class Cat(Animal):
    def run(self):
        print("Cat is running.")


class Husky(Animal, Animal2):  # 多继承,用逗号分隔开
    pass


dog = Dog()  # Dog类创建实例
dog.run()  # Dog类内未定义方法,实例直接调用父类的方法

# Dog is running.

理解多态,首先要对数据类型再进行说明。定义一个类的时候,实际上就是定义了一种数据类型。我们自定义的数据类型和Python自带的数据类型,比如str、list、dict没什么区别。

用isinstance()来判断某个变量是否是某个类型

对于一个变量,我们只要知道他的父类型,无需确切知道子类型,就可以放心调用相关的方法。运行时,具体的方法是作用在子类型上还是父类型上,由我们运行的对象决定。

也就是说,调用时,只管调用,不管细节。当我们新增一个子类时,只要保证相关的方法编写正确,就不用管原来的代码时如何调用的。

—>”开闭“原则

  • 对拓展开放:允许新增子类
  • 对修改封闭:不需要修改依赖父类类型的函数。

总结:

继承可以一级一级的继承下来,类比人类,就好比,爷爷奶奶到父母,再到子女

任何类都可以追溯到根类object。

私有属性

在类的内部,可以有属性和方法,而外部代码可以通过直接调用实例变量和方法来操作数据。这样,隐藏内部的复杂逻辑。

比如Student类:

class Student:
    school = 'zucc'
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def find_score(self):
        print(self.name, ":", self.score)


stu1 = Student("Tom", 99)
stu2 = Student("Jack", 89)
print(stu1.score)
stu1.score = 97
print(stu1.score)

# 99
# 97

从这可以看出,外部代码可以自由修改一个实例的属性(name,score)

如果要让内部属性不被外部访问,我们可以在属性的名称前面加两个下划线。

在Python中,实例的变量名如果以双下划线开头,就变成了一个私有变量,只有内部可以访问,外部不能访问。

封装

隐藏对象的属性和实现细节,仅对外提供公共访问的方式。

这样做的优点在于:

1.可以将变化格式;

2.便于使用;

3.提高整个数据的安全性;

4.提高复用性。

封装的原则是:

  • 将不需要对外提供的内容隐藏起来;
  • 隐藏属性,提供公共方法对其进行访问。

—> 私有方法,私有变量 —> 私有属性

用双下划线开头的方式将属性隐藏,设为私有的。

class Student:  # 定义父类
    school = 'zucc'

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

    def set_score(self, score):
        self.__score = score

    def find_score(self):
        print(self.__name, ":", self.__score)


stu1 = Student("Tom", 99)
print(stu1.get_score())
stu1.set_score(50)
print(stu1.get_score())
#99
#50

鸭子类型

鸭子类型不要求有严格的继承关系,一个对象,只要“看起来像鸭子,走起路来还是像鸭子”

也就是说,如果要编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为像的对象,但与其无任何关系的全新对象。

比方说,利用标准库中定义的各种“与文件类似的”的对象,尽管这些对象的工作方式像文件,但他们并没有继承内置文件对象的方法。

class TestFile:
    def read(self):
        pass
    def write(self):
        pass
class OperFile:
    def read(self):
        pass
    def write(self):
        pass

静态方法和类方法

静态方法

  • 通过装饰器@staticmethod 来进行装饰。静态方法既不需要传递类对象,也不需要传递实例对象
  • 静态方法也可以通过实例对象类对象去访问。
class Dog:
    type = '狗'
    def __init__(self):
        name = None
    # 静态方法
    @staticmethod
    def introduce():  # 静态方法不会自动传递实例对象和类对象
        print("犬科哺乳动物,属于肉食目.")

dog = Dog()
Dog.introduce()
dog.introduce()

静态方法是类中的函数,不需要实例。

静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,也就是说在静态方法中,不会涉及类中属性和方法的操作。

—> 可以理解为,静态方法是一个独立的,单纯的函数,仅仅是托管于某个类的命名空间中,便于维护和管理。

使用场景:

  • 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象(如类属性、类方法、创建实例等)时,定义静态方法
  • 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
  • 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。

类方法

  • 类对象所拥有的方法
  • 需要用装饰器@classmethod 来标识其为类方法。
  • 对于类方法,第一个参数必须是类对象,一般以cls 作为第一个参数
class Dog:
    __type = "狗"

    # 类方法,用classmethod来进行装饰
    @classmethod
    def get_type(cls):
        return cls.__type
print(Dog.get_type())
# 狗

使用场景:

  • 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法。
  • 类方法一般和类属性配合使用。

注意:

类中定义了同名的对象方法,类方法以及静态方法时,调用方法会优先执行最后定义的方法。

demo:

class Dog:
    def demo_method(self):
        print("对象方法")
    @classmethod
    def demo_method(cls):
        print("类方法")
    @staticmethod
    def demo_method():  # 最后被定义,调用时优先执行
        print("静态方法")
dog = Dog()
Dog.demo_method()
dog.demo_method()
# 静态方法
# 静态方法

property

概述

在Python中主要为属性提供一个便利的操作方式。

如果我们现在需要设计一个银行账户类,这个类中包含账户人的姓名,余额。

简单的实现:

class Account(object):

    def __init__(self, name, money):
        self.name = name
        self.money = money

问题:

不安全(设计简单方便,所有的属性,外部都可以访问修改,非常不安全)

改进1 隐藏实现细节

对于账户信息而言,金额不允许让用户直接修改。如果修改,只能去窗口办理。

程序的实现该如何去做?

在使用对象时,尽量不要让使用者直接操作对象中的属性,因为直接操作会带来安全隐患。

这个时候,考虑私有属性

class Account(object):

    def __init__(self, name, money):
        self.__name = name
        self.__money = money

代码改进以后,将所有的属性设计成私有属性后。确实从外部使用时,不知道内部的属性是什么,不能直接修改对象,隐藏了实现的细节。

但一个新的问题,如果确实需要对这两个属性进行修改,该怎么办?

改进2 提供一个精确的访问

添加方法,访问私有属性。

class Account(object):

    def __init__(self, name, money):
        self.__name = name
        self.__balance = money

    def get_name(self):
        return self.__name

    def set_balance(self, money):
        self.__balance  = money

    def get_balance(self):
        return self.__balance

经过修改,外部使用这个类的对象时,想使用对象中的属性,只能通过类中提供的 set/get 接口来操作,提高了程序的安全性。

这样,程序基本达到了设计需求,但是能不能更加完善呢?

如果在使用这个类的对象过程中,由于误操作,传入了不正常的数据,导致数据异常。该如何以避免这种情况发生呢?

比如:设置金额时出现了负数,或字符串,或其它类型的对象。

改进3 保证数据的有效性

set_balance方法中,对传入的数据进行有效判断,如果是无效数据,提示用户出错。

class Account(object):

    def __init__(self, name, money):
        self.__name = name
        self.__balance = money

    def get_name(self):
        return self.__name

    def set_balance(self, money):
        if isinstance(money, int):
            if money > 0:
                self.__balance  = money
            else:
                raise ValueError("输入金额不正确")
        else:
            raise ValueError("输入金额不是数字")

    def get_balance(self):
        return self.__balance

经过几版本的迭代,程序看上去越来越健壮,安全性也越来越高。

但是使用过程中,可不可以更精炼一些?

属性操作。

property类

在Python中,提供了一个property类,通过对创建这个类的对象的设置,在使用对象的私有属性时,可以不再使用属性的函数的调用方式,而是向普通的公有属性一样去使用属性,为开发者提供便利。

property(fget=None, fset=None, fdel=None, doc=None)  # property attribute

property 是一个类,__init__方法由四个参数组成,实例后返回一个用来操作属性的对象

  • 参数一:属性的获取方法
  • 参数二:属性的设置方法
  • 参数三:属性的删除方法
  • 参数四:属性描述
class Account(object):

    def __init__(self, name, money):
        self.__name = name
        self.__balance = money

    def __get_name(self):
        return self.__name

    def set_balance(self, money):
        if isinstance(money, int):
            if money > 0:
                self.__balance  = money
            else:
                raise ValueError("输入金额不正确")
        else:
            raise ValueError("输入金额不是数字")

    def get_balance(self):
        return self.__balance

    name = property(__get_name)
    balance = property(get_balance, set_balance)
ac = Account('Tom', 10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
# Tom
# 10000
# 1000

# 未使用property
ac = Account('Tom', 10000)  # 无法访问name,get_name 方法为私有方法
print(ac.get_balance())
ac.set_balance(1000)
print(ac.get_balance())
# 10000
# 1000

通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节 。通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节 。

改写property形式

class Account(object):

    def __init__(self, name, money):
        self.__name = name
        self.__balance = money

    @property
    def name(self):
        return self.__name

    @property
    def balance(self):
        return self.__balance

    @balance.setter
    def balance(self, money):
        if isinstance(money, int):
            if money > 0:
                self.__balance  = money
            else:
                raise ValueError("输入金额不正确")
        else:
            raise ValueError("输入金额不是数字")

ac = Account('Tom', 10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
# Tom
# 10000
# 1000

self

如果对象的方法中需要使用该对象的属性,该怎么办?

  • 关键字self主要用于对象方法中,表示调用该方法的对象。
  • 在方法中使用self,可以获取到调用当前方法的对象,进而获取该对象的属性和方法。

调用对象的方法时,为什么不需要设置self对应的参数?

  • 某个对象调用其方法时,Python解释器会把这个对象作为第一个参数传递给方法,所以,开发者只需要在定义的时候预留第一个参数self即可。
class Cat:
    # 方法
    def introduce(self):
        print("name is: %s, age is: %d" % (self.name, self.age))
cat = Cat()
cat.name = 'liyue'
cat.age = 11
cat.introduce()
# name is: liyue, age is: 11

方法内定义属性

  • 使用self操作属性和对象的变量名在效果上类似。如果属性在赋值时还没被定义。就会自动定义一个属性并赋值。
class Cat:
    def introduce(self):
        self.type = "小型动物"
cat = Cat()
cat.introduce()
print(cat.type)
# 小型动物

__new__方法

  • 创建对象时,系统会自动调用__new__方法。

  • 开发者可以使用__new__方法来自定义对象的创建过程。

  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

  • __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

  • __init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

  • 如果创建对象时传递了自定义参数,且重写了new方法,则new也必须 “预留” 该形参,否则init方法将无法获取到该参数

class A(object):
    def __new__(cls, x):
        print('this is in A.__new__, and x is ', x)
        return super(A, cls).__new__(cls)

    def __init__(self, y):
        print('this is in A.__init__, and y is ', y)

class C(object):
    def __new__(cls, n):
        print('this is in C.__new__, and n is ', n)
        return super(C, cls).__new__(cls)

    def __init__(self, a):
        print('this is in C.__init__, and a is ', a)


class B(A):
    def __new__(cls, z):
        print('this is in B.__new__, and z is ', z)
        return A.__new__(cls, z)

    def __init__(self, m):
        print('this is in B.__init__, and m is ', m)

# class B(A):
#     def __new__(cls, z):
#         print 'this is in B.__new__, and z is ', z
#         return object.__new__(cls)
#     def __init__(self, m):
#         print 'this is ni B.__init__, and m is ', m

if __name__ == '__main__':
    a = A(100)
    print('=' * 20)
    b = B(200)
    print(type(b))

__call__方法

__call__方法能够让类的实例对象,像函数一样被调用

对象后面加括号,触发执行

构造方法的执行是由创建对象触发的,即:对象=类名()

而对于,__call__方法的执行是由对象后加括号触发的,即对象()或者类()

class A(object):
    def __call__(self, x):
        print('__call__ called, print x: ', x)
a = A()
a('444')
# __call__ called, print x:  444

instance 和 issubclass

isinstance(obj,cls) 检查obj是否是类cls的对象

issubclass(Sub,Sup) 检查Sub是否是Sup的子类

class Foo:
    pass
foo = Foo()
print(isinstance(foo,Foo))  # True
print(isinstance(1,int))  # True
class Foo:
    pass
class Bar(Foo):
    pass
print(issubclass(Bar,Foo))  # True Bar is sub class of Foo

反射

所谓反射,是指程序可以访问、检测、修改本身状态或者行为的一种能力(自省)

在Python中,面向对象中的反射是指通过字符串的形式操作对象的相关属性。

四个可以实现自省的函数

  • hasattr
    • hasattr(*args, **kwargs)
  • getattr
    • getattr(obj, name, default=None)
  • setattr
    • setattr(x, y, v)
  • delattr
    • delattr(x, y)
class Foo:
    f = "类的静态变量"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print("My name is %s, my age is %d." % (self.name, self.age))


obj = Foo("liyue", 21)

# 检测是否含有某个属性
print(hasattr(obj, 'name'))  # True
print(hasattr(obj, 'f'))  # True
print(hasattr(obj, 'say'))  # True
# 获取属性
getattr(obj, 'say')()  # My name is liyue, my age is 21.
print(getattr(obj, 'age'))  # 21
# print(getattr(obj, 'hello'))  # 不存在,报错
print(getattr(obj, 'hello', 1))  # 1
# 设置属性
print(obj.__dict__)  # {'name': 'liyue', 'age': 21}
setattr(obj, 'good man', True)
setattr(obj, 'show_name', lambda self: self.name + 'good man')
print(obj.__dict__)  # {'name': 'liyue', 'age': 21, 'good man': True, 'show_name': <function <lambda> at 0x000001E15EE03E18>}
print(obj.show_name(obj))  # liyuegood man
# 删除属性
delattr(obj, 'show_name')
print(obj.__dict__)  # {'name': 'liyue', 'age': 21, 'good man': True}
# delattr(obj, 'hello')  # 不存在,报错

item系列

联想,字符串、列表、元组可以通过索引值去引用值,字典可以通过关键字引用值,引用[].

class Foo:
    def __init__(self, name):
        self.name = name
    def __getitem__(self, item):
        print(self.__dict__[item])
    def __setitem__(self, key, value):
        self.__dict__[key] = value
    def __delitem__(self, key):
        print("del obj[key]时:")
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print("del obj.key时:")
        self.__dict__.pop(item)
obj1 = Foo('obj1')
obj1['age'] = 18
obj1['age1'] = 19
print(obj1.__dict__)  # {'name': 'obj1', 'age': 18, 'age1': 19}
del obj1['age']  # del obj[key]时:
del obj1.age1  # del obj.key时:
print(obj1.__dict__)  # {'name': 'obj1'}
obj1['name'] = 'Jack'
print(obj1.__dict__)  # {'name': 'Jack'}

__del__

析构方法,当对象在内存中被释放,就会自动触发执行。

一般不需要自定义。

因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo:
    def __del__(self):
        print("执行Del函数了。")
obj = Foo()
del obj  # 执行Del函数了。

__str__

  • 如果直接print打印对象。会看到创建对象的内存地址。

  • 当我们使用print(XXX)时,输出对象,如果对象中定义了__str__方法,就会打印该方法return的信息描述。

class Cat:
    def __init__(self, new_name, new_age):
        self.name = new_name
        self.age = new_age
    def __str__(self):
        return '名字是 %s,年龄是%d' % (self.name, self.age)
cat = Cat('Tom', 5)
print(cat)  # 名字是 Tom,年龄是5
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章