Python基础语法07--面向对象

本文详细介绍了Python的面向对象编程,包括面向过程与面向对象的对比、类与对象的概念、类的创建和对象的实例化、类属性、类方法、静态方法、动态绑定属性和方法。同时,深入探讨了面向对象的三大特征:封装、继承和方法重写,并通过实例解析了Python中子类执行构造方法的三种情况、多态性以及特殊方法和特殊属性。此外,还讨论了类的浅拷贝与深拷贝的差异。

目录

1、编程的两大思想,面向过程 VS 面向对象

2、类与对象

1)类

2)数据类型

3)对象

3、类的创建

1)创建类

2)类的组成

4、对象的创建

5、类属性,类方法,静态方法

6、动态绑定属性和方法

1)动态绑定属性

2)动态绑定方法

7、总结

二、面向对象的三大特征

1、封装

属性不希望在类对象外部被访问,前边使用两个“_”

2、继承

多继承,一个子类可以有多个父类

3、方法重写

其他补充

Python子类执行构造方法的三种情况

4、object类

5、多态

6、特殊方法和特殊属性

1)特殊属性

2)特殊方法

7、类的浅拷贝与深拷贝

1)变量的赋值操作

2)浅拷贝

3)深拷贝

三、总结


1、编程的两大思想,面向过程 VS 面向对象

1)面向过程和面向对象的区别

  • 面向过程,事物比较简单,可以用线性的思维去解决;
  • 面向对象,事物比较复杂,使用简单的线性思维无法解决;

 

2)面向过程和面向对象的共同点

  • 都是解决实际问题的一种思维方式;

 

3)二者之间的关系:

  • 解决复杂问题,通过面向对象方式,便于从宏观把握事物之间复杂的关系,方便分析整个系统;
  • 具体到微观操作,仍然使用面向过程方式来处理;
  • 如:想吃西红柿炒蛋,自己购买材料去做是面向过程;外卖下单购买,厨房接单制作,是面向对象;

 

2、类与对象

1)类

  • 类别,多个类似事物组成的群体的统称,如人类,鸟类,动物类等;

 

2)数据类型

  • 不同的数据类型属于不同的类
  • 使用内置函数查看数据类型;
# 不同的数据类型属于不同的类
print(type(100))  # <class 'int'>
print(type(50))  # <class 'int'>
print(type(10))  # <class 'int'>

 

3)对象

  • 100,50,10都是int类之下包含的相似的不同个例,这个个例称为实例或对象
  • python中,一切皆对象;如字符串对象‘hello’,整数对象520,字典对象{'key':value},元组对象(10,20),列表对象[10,20]等;

 

3、类的创建

语法:

class Student:
    pass

 

1)创建类

  • Student为类的名称,由一个或多个单词组成,每个单词的首字母大写,其余小写;
  • 类也占用内存空间,也有类型,也有value值;
# 创建类对象
class Student:
    pass

print(id(Student))  # 占用了内存空间,1972126470016
print(type(Student))  # class类型,<class 'type'>
print(Student)  # value值是<class '__main__.Student'>

 

2)类的组成

  • 类属性,直接写在类里面的变量;
  • 实例方法,类外定义的是函数,在类内定义的是方法
  • 静态方法
  • 类方法

 

创建学生类

# 创建学生类
class Student:
    native_place = '吉林'  # 类属性

    def __init__(self, name, age):   # 初始化方法
        self.name = name  # self.name是实例属性
        self.age = age  # 将局部变量age的值,赋值给self.age实例属性

    # 实例方法
    def info(self):  # 传递的是类的对象self
        print('我的名字叫:', self.name, '年龄是:', self.age)

    # 类方法
    @classmethod
    def cm(cls):  # 传递的是class
        print('这是类方法')

    # 静态方法
    @staticmethod
    def sm():  # 静态方法不允许传递值
        print('这是静态方法')

 

 

类外定义的是函数,在类内定义的是方法

# 类的方法和函数
# 类的方法
class Student:
    def eat(self):
        print('这是类的方法')

# 函数
def drink():
    print('这是函数')

 

4、对象的创建

  • 对象的创建,又称为类的实例化,类的实例创建;
  • 有了实例,就可以调用类中的内容;

语法:

实例名 = 类名()

参考学生类 

stu = Student('张三', 20)
print(id(stu))  # id = 1413659559584
print(type(stu))  # 对象的类型,<class '__main__.Student'>
print(stu)  # 对象的id对应的16进制的地址,<__main__.Student object at 0x0000014924AD06A0>

print('-----------------')
print(id(Student))  # 2445954439664
print(type(Student))  # <class 'type'>
print(Student)  # <class '__main__.Student'>

 输出结果:

2443846615488
<class '__main__.Student'>
<__main__.Student object at 0x00000239009C01C0>
-----------------
2445954439664
<class 'type'>
<class '__main__.Student'>

 

Student是类对象,stu是实例对象;

 

调用方法的2种写法:

  • 对象名.方法名(),如:stu.info() 
  • 类名.方法名(类的对象),实际就是方法定义处的self,如:Student.info(stu)
# 创建学生类
class Student:
    native_place = '吉林'  # 类属性

    def __init__(self, name, age):   # 初始化方法
        self.name = name  # self.name是实例属性
        self.age = age  # 将局部变量age的值,赋值给self.age实例属性

    # 实例方法
    def info(self):  # 传递的是类的对象self
        print('我的名字叫:', self.name, '年龄是:', self.age)

    # 类方法
    @classmethod
    def cm(cls):  # 传递的是class
        print('这是类方法')

    # 静态方法
    @staticmethod
    def sm():  # 静态方法不允许传递值
        print('这是静态方法')


# 创建Student类的实例对象
stu = Student('jack', 20)
print(stu.name)  # 实例属性
print(stu.age)  # 实例属性
stu.info()  # 调用实例方法

print('-------------------')
Student.info(stu)  # 调用实例方法,另外一种写法

输出结果:

jack
20
我的名字叫: jack 年龄是: 20
-------------------
我的名字叫: jack 年龄是: 20

 

5、类属性,类方法,静态方法

  • 类属性:类中方法外的变量称为类属性,被该类的所有对象所共享
  • 类方法:使用@classmethod修饰的方法,使用类名直接访问的方法
  • 静态方法:使用@staticmethod修饰的方法,使用类名直接访问的方法
# 创建Student类的实例对象
print(Student.native_place)  # 访问类属性
Student.cm()  # 调用类方法
Student.sm()  # 调用静态方法

输出结果:

吉林
这是类方法
这是静态方法

 

类属性的使用方法

# 类属性的使用方法
print(Student.native_place)
stu1 = Student('张三', 10)
stu2 = Student('李四', 20)
print(stu1.native_place)
print(stu2.native_place)

print('-----修改类属性的值---------')
Student.native_place = '天津'
print(Student.native_place)
print(stu1.native_place)
print(stu2.native_place)

输出结果:

吉林
吉林
吉林
-----修改类属性的值---------
天津
天津
天津

 

6、动态绑定属性和方法

  • python是动态语言,在创建对象之后,可以动态的绑定属性和方法

 

  • 一个Student类,可以创建N多个Student类的实例对象,每个实例对象的属性值不同;即stu1,stu2
  • 一个Student类,只有一个类对象,即Student类对象

 

需求:只为stu1动态绑定性别属性,指向女;

  • 创建对象stu1后,单独给这个对象stu1绑定一个属性gender;所以stu2是没有这个属性的;

1)动态绑定属性

# 动态绑定属性
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print(self.name + '在吃饭')

stu1 = Student('张三', 20)
stu2 = Student('李四', 30)

print('---------只为stu1动态绑定性别属性,指向女----------')
stu1.gender = '女'
print(stu1.name, stu1.age, stu1.gender)
print(stu2.name, stu2.age)
# print(stu2.gender)  # stu2没有绑定属性gender,会报错,AttributeError: 'Student' object has no attribute 'gender'

输出结果:

张三 20 女
李四 30

 

2)动态绑定方法

# 动态绑定方法
class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print(self.name + '在吃饭')

stu1 = Student('张三', 20)
stu2 = Student('李四', 30)

stu1.eat()
stu2.eat()

print('---------只为stu1动态绑定show()方法----------')
def show_stu1():
    print('定义在类之外的,是函数')

stu1.show = show_stu1
stu1.show()
# stu2.show()  # stu2没有绑定show()方法,所以调用show()会报错,AttributeError: 'Student' object has no attribute 'show'

输出结果:

张三在吃饭
李四在吃饭
---------只为stu1动态绑定show()方法----------
定义在类之外的,是函数

 

为stu1动态绑定属性和方法的示意图:

 

7、总结

 

二、面向对象的三大特征

1、封装

  • 提高程序的安全性;
  • 数据(属性)和行为(方法)包装到类对象中;
  • 方法内部对属性进行操作,在类对象的外部调用方法
  • 无需关心方法内部的具体实现细节,从而隔离了复杂度;
  • python中没有专门的修饰用于属性的私有,如果该属性不希望类对象外部被访问,前边使用两个“_”
# 封装
# 对汽车进行封装
class Car:
    def __init__(self, brand):
        self.brand = brand

    def start(self):
        print(self.brand, '品牌的车启动了')

car = Car('宝马X5')
car.start()
print(car.brand)

输出结果:

宝马X5 品牌的车启动了
宝马X5

 

属性不希望类对象外部被访问,前边使用两个“_”

  • 私有属性,不能直接被访问,报错,AttributeError: 'Student' object has no attribute '__age'
  • 在类的外部可以通过 _类名__变量进行访问私有的属性,如_Student__age
# 属性不希望在类对象外部被访问,前边使用两个“_”
class Student:
    def __init__(self, name, age):
        self.name = name
        self.__age = age  # age不希望在类的外部被使用,所有加了两个_

    def show(self):
        print(self.name, self.__age)

stu = Student('张三', 20)
print(stu.name)
# print(stu.__age)  # 私有属性,不能直接被访问,报错,AttributeError: 'Student' object has no attribute '__age'
stu.show()

print(dir(stu))  # 查看stu对象的所有属性
print(stu._Student__age)  # 在类的外部可以通过 _类名__变量进行访问私有的属性,如_Student__age

输出结果:

张三
张三 20
['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'show']
20

 

2、继承

  • 提高代码的复用性;
  • 如果一个类没有继承任何类,则默认继承object
  • python支持多继承
  • 定义子类时,必须在子类的构造函数调用父类的构造函数

语法:

class 子类类名(父类1,父类2……):
    pass
# 继承
class Person(object):  # Person类继承object类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print(self.name, self.age)

class Student(Person):
    def __init__(self, name, age, stu_no):
        super().__init__(name, age)
        self.stu_no = stu_no

class Teacher(Person):
    def __init__(self, name, age, teachofyear):
        super().__init__(name, age)
        self.teachofyear = teachofyear

stu = Student('张学生', 10, '1001')
teacher = Teacher('李老师', 20, 5)

stu.info()
teacher .info()

输出结果:

张学生 10
李老师 20

 

多继承,一个子类可以有多个父类

语法格式:

# 多继承
class A(object):
    pass

class B(object):
    pass

class sub(A, B):
    pass

 

3、方法重写

  • 如果子类对继承自父类的某个属性方法不满意,可以在子类中对其(方法体)进行重新编写
  • 子类重写后的方法中,可以通过super().xxx()调用父类中被重写的方法
  • super().__init__(name, age)  # 去执行父类中的构造方法,传入父类构造方法需要的参数
  • super().info()  # 去执行父类中的方法
# 方法重写
class Person(object):  # Person类继承object类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        print(self.name, self.age)

class Student(Person):
    def __init__(self, name, age, stu_no):
        super().__init__(name, age)  # 去执行父类中的构造方法
        self.stu_no = stu_no  # 执行子类自己的代码

    def info(self):  # 子类中方法重写
        super().info()  # 去执行父类中的方法
        print('学号:{0}'.format(self.stu_no))  # 执行子类自己的代码

class Teacher(Person):
    def __init__(self, name, age, teachofyear):
        super().__init__(name, age)
        self.teachofyear = teachofyear

    def info(self):  # 子类中方法重写
        super().info()
        print('教龄:{0}'.format(self.teachofyear))

stu = Student('张学生', 10, '1001')
teacher = Teacher('李老师', 20, 5)

stu.info()
teacher .info()

输出结果:

张学生 10
学号:1001
李老师 20
教龄:5

 

其他补充

类内部定义的变量,只在实例化后的对象中有效,哪个实例修改了,哪个实例可以获取到修改后的值,不影响其他未做修改的实例;

class Father(object):
    var = 10

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

if __name__ == '__main__':
    father1 = Father('小明')
    father2 = Father('小马')
    father1.var = 20
    print("father1.var的值为:", father1.var)
    print("father2.var的值为:", father2.var)

输出结果:father1对象修改了var的值,father1获取var值时,就是20;但是不影响father2的var的值;

father1.var的值为: 20
father2.var的值为: 10

 

  • 类或类的方法中定义的变量,都可以通过类的实例化对象进行修改;
  • 一个对象修改了属性的值,不会影响另外一个对象对属性的取值,另外一个对象取到的还是未修改前的值;
  • 在非构造函数中定义的属性,如self.test1,因为自定义的方法,不会在运行时自动加载,所以未运行test()方法前取属性test1的值,会报错;运行过test()方法后,再取属性test1的值,就不会报错;
class Father(object):
    var = 10
    def __init__(self, name):
        self.name = name
        self.age = 18

    def test(self):
        self.test1 = 'test1'
        print("self.test1值为:", self.test1)
        return "test方法返回字符串"


if __name__ == '__main__':
    father = Father('小明')
    father.var = 20
    father.name = '小红'
    father.age = 30
    father.test1 = 'test2'
    print("father.var的值为:", father.var)
    print("father.name的值为:", father.name)
    print("father.age的值为:", father.age)
    print("father.test1的值为:", father.test1)
    
    father2 = Father('小黄')
    print("father2.var的值为:", father2.var)
    print("father2.name的值为:", father2.name)
    print("father2.age的值为:", father2.age)
    print("father2.test1的值为:", father2.test1)  # 报错'Father' object has no attribute 'test1'

输出结果:father对象中的属性取值,都是修改后的值;father2对象中的属性取值,还是未修改前的值,即father改变了属性值,father2不会被影响,取到的不是修改后的值;

father.var的值为: 20
father.name的值为: 小红
father.age的值为: 30
father.test1的值为: test2


father2.var的值为: 10
father2.name的值为: 小黄
father2.age的值为: 18

 

  • 子类的对象可以引用父类的属性和方法;
  • 但是子类中定义了构造方法后,就不能直接引用父类的构造方法中定义的属性了;
class Father(object):
    var = 10

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

    def test(self):
        self.test1 = 'test1'
        print("self.test1值为:", self.test1)
        return "test方法返回字符串"

class Son(Father):
    def __init__(self):
        self.sex = '男'

if __name__ == '__main__':
    son = Son()
    print("son.sex的值为:", son.sex)
    print("son.var的值为:", son.var)
    # print("son.name的值为:", son.name)  # 'Son' object has no attribute 'name'
    # print("son.age的值为:", son.age)  # 'Son' object has no attribute 'age'
    print(son.test())
    print("son.test1的值为:", son.test1)

输出结果:son对象可以引用父类的var属性,不能引用父类的name和age属性;son对象可以引用父类的test()方法,以及方法中定义的属性test1

son.sex的值为: 男
son.var的值为: 10
self.test1值为: test1
test方法返回字符串
son.test1的值为: test1

 

Python子类执行构造方法的三种情况

  • 1. 子类不重写 __init__,实例化子类时,会自动调用父类定义的 __init__。
  • 2. 如果重写了__init__ 时,实例化子类,就不会调用父类已经定义的 __init__。
  • 3. 如果重写了__init__ 时,要继承父类的构造方法,可以使用 super 关键字。

 

重写了__init__ 时,要继承父类的构造方法,可以使用 super 关键字:2种写法都可以

super(子类,self).__init__(参数1,参数2,....)

父类名称.__init__(self,参数1,参数2,...)

 

子类中定义了构造方法,如果还想要能够访问父类中的构造方法,可以有如下几种写法:

super(Son, self).__init__(son_name)  # 执行父类的构造方法;传入父类构造函数需要的参数son_name,并且子类的构造函数也要传入son_name

Father.__init__(self, son_name)  # 执行父类的构造方法

 

super(子类,self).__init__(参数1,参数2,....)​​​​​​​

class Father(object):
    def __init__(self, name):
        self.name = name
        self.age = 18

class Son(Father):
    def __init__(self, son_name):
        super(Son, self).__init__(son_name)  # 执行父类的构造方法
        self.sex = '男'

if __name__ == '__main__':
    son = Son("儿子")
    print("son.name的值为:", son.name)  # 'Son' object has no attribute 'name'
    print("son.age的值为:", son.age)  # 'Son' object has no attribute 'age'

输出结果:

son.name的值为: 儿子
son.age的值为: 18

 

父类名称.__init__(self,参数1,参数2,...)

class Father(object):
    def __init__(self, name):
        self.name = name
        self.age = 18

class Son(Father):
    def __init__(self, son_name):
        Father.__init__(self, son_name)  # 执行父类的构造方法
        self.sex = '男'

if __name__ == '__main__':
    son = Son("儿子")
    print("son.name的值为:", son.name)  # 'Son' object has no attribute 'name'
    print("son.age的值为:", son.age)  # 'Son' object has no attribute 'age'

输出结果:

son.name的值为: 儿子
son.age的值为: 18

 

4、object类

  • object 类是所有类的父类,因此所有类都有object类属性和方法
  • 内置函数dir(),可以查看指定对象所有属性
  • object类的__str__()方法,用于返回一个对象描述,对应于内置函数str(),经常用于print()方法,查看对象的信息,所以__str__()经常会被重写;
# object类
class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '我的名字是{0},我的年龄是{1}'.format(self.name, self.age)

stu = Student('张三', '20')
print(dir(stu))
# print(stu)  # 重写方法前输出:<__main__.Student object at 0x0000024DBB6B4700>
print(stu)  # 重写方法后输出:我的名字是张三,我的年龄是20
print(type(stu))

输出结果:子类中,__str__方法被重写了

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
我的名字是张三,我的年龄是20
<class '__main__.Student'>

 

5、多态

  • 提高程序的可扩展性和可维护性;
  • 即具有多种形态,指即便不知道一个变量所引用的对象到底是什么类型仍然可以通过这个变量调用方法在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法

 

静态语言和动态语言

1)静态语言和动态语言关于多态的区别

静态语言实现多态的三个必要条件:如java

  • 明确继承关系
  • 方法重写
  • 父类引用指向子类对象

动态语言的多态,是鸭子类型,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为;即不用关心Person是谁的子类,只要Person有eat方法就可以了;

# 多态
class Animal(object):
    def eat(self):
        print('动物要吃东西')

class Dog(Animal):
    def eat(self):
        print('狗要吃肉')

class Cat(Animal):
    def eat(self):
        print('猫吃鱼')

class Person(object):
    def eat(self):
        print('人吃五谷杂粮')

# 定义一个函数,可以传入任意对象
def fun(animal):
    animal.eat()  # 对象调用eat()方法

# 开始调用函数
fun(Dog())
fun(Cat())
fun(Animal())
fun(Person())

输出结果:

狗要吃肉
猫吃鱼
动物要吃东西
人吃五谷杂粮

 

6、特殊方法和特殊属性

  • 两个_开始和两个_结束的属性和方法;

 

1)特殊属性

  • dir() 查看object类对象的属性和方法
  • __dict__查看实例对象的所有实例属性,或查看类对象的所有属性和方法的字典;
  • __class__显示对象所属的类型
  • __bases__输出类对象的父类的类型,返回元组
  •  __base__输出与C类最近的一个父类
  • __mro__输出类的层次结构,返回元组
  • __subclasses__()输出子类列表, 返回列表

 

dir() 查看object类对象的属性和方法

# 特殊属性
print(dir(object))  # dir() 查看object类对象的属性和方法

输出结果:

['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
 

 

创建类

class A:
    pass

class B:
    pass

class C(A, B):
    lst = ['hello']
    def __init__(self,name, age):
        self.name = name
        self.age = age

    def info(self):
        pass

class D(A):
    pass

 

1>> __dict__查看实例对象的所有实例属性,或查看类对象的所有属性和方法的字典

# __dict__查看实例对象的所有实例属性,或查看类对象的所有属性和方法的字典
x = C('jack', 10)
print(x.__dict__)  # C类的实例对象c,绑定的所有实例属性,返回字典,{'name': 'jack', 'age': 10}
print(C.__dict__)  # C类对象绑定的所有属性和方法,返回字典

输出结果:
{'name': 'jack', 'age': 10}
{'__module__': '__main__', 'lst': ['hello'], '__init__': <function C.__init__ at 0x000001C06F2EA550>, 'info': <function C.info at 0x000001C06F2EA5E0>, '__doc__': None}
 

2>> __class__显示对象所属的类型

# __class__显示对象所属的类型
print(x.__class__)  # __class__显示对象所属的类型,<class '__main__.C'>
print(C.__class__)  # <class 'type'>

输出结果:

<class '__main__.C'>
<class 'type'>

 

3>> __bases__输出类对象的父类的类型,返回元组

# 输出类对象的父类的类型,返回元组
# print(x.__bases__)  # 实例对象没有__bases__属性,会报错AttributeError: 'C' object has no attribute '__bases__'
print(C.__bases__)  # (<class '__main__.A'>, <class '__main__.B'>)

输出结果: 

(<class '__main__.A'>, <class '__main__.B'>)

 

4>> __base__输出与C类最近的一个父类

# 输出与C类最近的一个父类
print(C.__base__)  #  class C(A,B) 只返回<class '__main__.A'>
print(C.__base__)  #  class C(B,A) 只返回<class '__main__.B'>

 

5>> __mro__输出类的层次结构,返回元组

# __mro__输出类的层次结构,返回元组
print('------输出类的层次结构--------')
print(C.__mro__)  # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print(D.__mro__)  # (<class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
print(A.__mro__)  # (<class '__main__.A'>, <class 'object'>)
print(B.__mro__)  # (<class '__main__.B'>, <class 'object'>)
print(object.__mro__)  # (<class 'object'>,)

输出结果: 

------输出类的层次结构--------
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
(<class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
(<class '__main__.A'>, <class 'object'>)
(<class '__main__.B'>, <class 'object'>)
(<class 'object'>,)

 

6>> __subclasses__()输出子类列表, 返回列表

# __subclasses__()输出子类列表, 返回列表
print('------输出子类--------')
print(A.__subclasses__())  # [<class '__main__.C'>, <class '__main__.D'>]
print(B.__subclasses__())  # [<class '__main__.C'>]
print(C.__subclasses__())  # []
print(D.__subclasses__())  # []
print('object的子类有:', object.__subclasses__())  # <class '__main__.C'>, <class '__main__.D'>不是object的子类

 输出结果:

------输出子类--------
[<class '__main__.C'>, <class '__main__.D'>]
[<class '__main__.C'>]
[]
[]
object的子类有: [<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>, <class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>, <class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>, <class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'pickle.PickleBuffer'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <class 'moduledef'>, <class 'module'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'nt.ScandirIterator'>, <class 'nt.DirEntry'>, <class 'PyHKEY'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>, <class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>, <class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class 'os._AddedDllDirectory'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>, <class 'MultibyteCodec'>, <class 'MultibyteIncrementalEncoder'>, <class 'MultibyteIncrementalDecoder'>, <class 'MultibyteStreamReader'>, <class 'MultibyteStreamWriter'>, <class '__main__.A'>, <class '__main__.B'>]
 

 

2)特殊方法

  • __len__()  通过重写__len__()方法,让内置函数len的参数可以是自定义类型
  • __add__()  通过重写__add__() 方法,可使用自定义对象具有"+"功能
  • __new__()  用于创建对象;
  • __init__()  对创建的对象进行初始化;

 

1>>  __add__()  通过重写__add__() 方法,可使用自定义对象具有"+"功能;

# 特殊方法__add__()
a = 20
b = 100
c = a + b  # 两个整数类型的对象相加运算
d = a.__add__(b)  # 两个整数类型的对象相加运算
print(c)
print(d)

# 自定义类型如类的实例对象,让2个对象执行相加操作,要重写__add__()方法
class Student:
    def __init__(self, name):
        self.name = name

    def __add__(self, other):  # concatenate str to str
        return self.name + other.name


stu1 = Student('jack')
stu2 = Student('lucy')
s = stu1 + stu2  # __add__()方法,实现了2个对象的加法运算
print(s)  # 如果Student类没有重写__add__()方法,会报错,TypeError: unsupported operand type(s) for +: 'Student' and 'Student'

print(stu1.__add__(stu2))

输出结果:

120
120
jacklucy
jacklucy
 

2>> 输出对象的长度,重写__len__()方法

# 特殊方法__len__
lst = [1, 2, 3]
print(len(lst))  # len()内置函数,计算列表的长度
print(lst.__len__())  # 特殊方法__len__,计算列表的长度

# 输出对象的长度,重写__len__()方法
class Student:
    def __init__(self, name):
        self.name = name

    def __len__(self):
        return len(self.name)

x = Student('jacky1')
# print(len(x))  # 报错,TypeError: object of type 'Student' has no len()

print(len(x))  # 重写__len__()方法后,不会报错,输出对象name的长度
print(x.__len__())  # 重写__len__()方法后,不会报错,输出对象name的长度

输出结果:

3
3
6
6

 

3>> __new__()  用于创建对象;

  • __init__()  对创建的对象进行初始化;
# 特殊方法__new__和__init__
class Person(object):
    # __new__用于创建对象
    def __new__(cls, *args, **kwargs):
        print('__new__(cls, *args, **kwargs)被调用了,cls是Person类对象,cls的id值为{0}'.format(id(cls)))  # Person类对象,2960168107376
        obj = super().__new__(cls)  # 调用父类object的创建对象,
        print('创建的Person类的实例对象id为{0}'.format(id(obj)))  # Person类的实例对象,2960206267968
        return obj

    # __init__给实例对象的属性进行初始化
    def __init__(self, name, age):
        print('__init__(self, name, age)被调用了,self是实例对象,self的id值为{0}'.format(id(self)))   # Person类的实例对象,2960206267968
        self.name = name
        self.age = age

print('object类对象的id为:{0}'.format(id(object)))  # object类对象,140718298803024
print('Person类对象的id为:{0}'.format(id(Person)))  # Person类对象,2960168107376

# 创建Person类的实例对象
print('\n-------创建Person类的实例对象前-----------')
p = Person('jack', 20)
print('\n-------创建Person类的实例对象后-----------')
print('Person类实例对象p的id为:{0}'.format(id(p)))  # Person类的实例对象,2960206267968

输出结果:

object类对象的id为:140718298803024
Person类对象的id为:2960168107376

-------创建Person类的实例对象前-----------
__new__(cls, *args, **kwargs)被调用了,cls是Person类对象,id值为2960168107376
创建的Person类的实例对象id为2960206267968
__init__(self, name, age)被调用了,self是实例对象,self的id值为2960206267968

-------创建Person类的实例对象后-----------
Person类实例对象p的id为:2960206267968

 

7、类的浅拷贝与深拷贝

1)变量的赋值操作

  • 只是形成两个变量,实际上还是指向同一个对象

 

类对象的赋值操作

  • 通过CPU类创建了CPU类的实例对象
  • 实例对象赋值给变量cpu1,cpu2

# CPU类
class CPU:
    pass

# 硬盘类
class Disk:
    pass

# CPU和硬盘组装成计算机
class Computer:
    def __init__(self, cpu, disk):
        self.cpu = cpu
        self.disk = disk


# 1、变量的赋值
cpu1 = CPU()
cpu2 = cpu1
cpu3 = CPU()
print(cpu1, id(cpu1))  # <__main__.CPU object at 0x000002660F4C87C0> 2637366593472
print(cpu2, id(cpu2))  # <__main__.CPU object at 0x000002660F4C87C0> 2637366593472
print(cpu3, id(cpu3))  # <__main__.CPU object at 0x000002660F49F820> 2637366425632

输出结果:

<__main__.CPU object at 0x000002660F4C87C0> 2637366593472
<__main__.CPU object at 0x000002660F4C87C0> 2637366593472
<__main__.CPU object at 0x000002660F49F820> 2637366425632

 

2)浅拷贝

  • python拷贝,一般是浅拷贝(没有特殊说明时)
  • 拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
  • 拷贝时,只拷贝子对象的引用,不拷贝子对象的内容;

import copy
computer2 = copy.copy(computer)

# 类有浅拷贝
class CPU:
    pass

class Disk:
    pass

class Computer:
    def __init__(self, cpu, disk):
        self.cpu = cpu
        self.disk = disk

cpu1 = CPU()  # 创建一个CPU类的对象
disk = Disk()  # 创建一个硬盘类的对象
computer = Computer(cpu1, disk)  # 创建一个计算机类的对象

# 浅拷贝
import copy
computer2 = copy.copy(computer)
# computer和computer2是不同的对象,所包含的子对象cpu和disk,却是相同的对象
print(id(cpu1))
print(id(disk))
print(id(computer), id(computer.cpu), id(computer.disk))
print(id(computer2), id(computer2.cpu), id(computer2.disk))
print(computer is computer2)  # False,浅拷贝后,computer2与computer不同
print(computer.cpu is computer2.cpu)  # True,浅拷贝后,computer2的子对象cpu与computer的子对象disk相同
print(computer.disk is computer2.disk)  # True,浅拷贝后,computer2的子对象disk与computer的子对象disk相同

输出结果

2724301277120
2724301109280
2724301107840 2724301277120 2724301109280
2724301776880 2724301277120 2724301109280

False
True
True

 

3)深拷贝

  • 使用copy模块deepcopy函数递归拷贝对象中包含的子对象;
  • 源对象和拷贝对象所有的子对象也不相同

# 类有深拷贝
class CPU:
    pass

class Disk:
    pass

class Computer:
    def __init__(self, cpu, disk):
        self.cpu = cpu
        self.disk = disk

cpu1 = CPU()  # 创建一个CPU类的对象
disk = Disk()  # 创建一个硬盘类的对象
computer = Computer(cpu1, disk)  # 创建一个计算机类的对象

# 深拷贝
import copy
computer2 = copy.deepcopy(computer)
# computer和computer2是不同的对象,所包含的子对象cpu和disk,也是不同的对象
print(id(cpu1))
print(id(disk))
print(id(computer), id(computer.cpu), id(computer.disk))
print(id(computer2), id(computer2.cpu), id(computer2.disk))
print(computer is computer2)  # False,深拷贝后,computer2与computer不同
print(computer.cpu is computer2.cpu)  # False,深拷贝后,computer2的子对象cpu与computer的子对象disk不同
print(computer.disk is computer2.disk)  # False,深拷贝后,computer2的子对象disk与computer的子对象disk不同

输出结果:

2724301277120
2724301109280
2724301107840 2724301277120 2724301109280
2724301776880 2724301277916 2724301103924
False
False
False

 

三、总结

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值