Python基础(类与对象)

Python基础(类与对象)

一、编程的两大思想

面向过程和面向对象

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

共同点:面向对象和面向过程都是解决实际问题的一种思维方式

二者相辅相成,并不是对立的,解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系,方便我们分析整个系统;具体到微观操作,仍然使用面向过程方式来处理

二、类

类别,分门别类,物以类聚,人类,鸟类,动物类,植物类…

  • 类时多个类似事物组成的群体的统称。能够帮助我们快速理解和判断事物的性质

三、定义Python中的类

创建类的语法

class Student: #Student为类的名称(类名),由一个或多个单词组成,每个丹迪的首字母大写,其余小写

​ pass

class Student: #Student为类的名称(类名),由一个或多个单词组成,每个丹迪的首字母大写,其余小写
    pass
class Student: #Student为类的名称(类名),由一个或多个单词组成,每个丹迪的首字母大写,其余小写
    pass

print(id(Student))
print(type(Student))
print(Student)

输出:

1512775104816
<class 'type'>  # 表明是class类型
<class '__main__.Student'>

类的组成

  • 类属性
  • 实例方法
  • 静态方法
  • 类方法
class Student:
    native_pace='吉林' #直接写在类里的变量,称为类属性

    def __init__(self,name,age):
        self.name=name #self.name,称为实体属性,进行了一个赋值的操作,将局部变量的name的值赋给实体属性
        self.age=age

    #实例方法
    def eat(self):
        print('学生在吃饭...')

    #静态方法
    @staticmethod
    def method():
        print('我使用了staticmethod进行修饰,所以我是静态方法')
	
    #类方法
    @classmethod
    def cm(cls):
        print('我是类方法,因为我使用了classmethod进行修饰')

#在类之外定义的称为函数,在类之内定义的称为方法
def drink():
    print('喝水')

四、对象的创建

  • 对象的创建又称为类的实例化
  • 语法:
    • 实例名=类名
  • 例子:
    • stu=Student()
  • 意义:有了实例,就可以调用类中的内容
#创建Student类的对象
stu1=Student('张三',20)
print(id(stu1))
print(type(stu1))
print(stu1)

输出:

1668138946320  #转换成十六进制就是  18464D44F10
<class '__main__.Student'>
<__main__.Student object at 0x0000018464D44F10>

查看类对象

print(id(Student))
print(type(Student))
print(Student)

输出:

2460596968352
<class 'type'>
<class '__main__.Student'>

对比上面实例对象和类对象的不同

五、使用对象

调用类的方法和属性

#创建Student类的对象
stu1=Student('张三',20)
stu1.eat()  #对象名.方法名()
print(stu1.name)  #调用实例对象的属性
print(stu1.age)

print('--------')
Student.eat(stu1) #这一行与上面调用eat()方法的功能相同,都是调用Student的eat()方法
                            #类名.方法名(类的对象)-->实际上就是方法定义处的self

输出:

学生在吃饭...
张三
20
--------
学生在吃饭...

对比两种调用方法的方式的不同,其实功能是相同的

六、类属性_类方法__类静态方法

类属性:类中方法外的变量称为类属性,被该类的所有对象所共享

类方法:使用@classmethod修饰的方法,使用类名直接访问的方法

静态方法:使用@staticmethod修饰的方法,使用类名直接访问的方法

print(Student.native_place) #访问类属性

Student.cm() # 调用类方法

Student.sm() # 调用静态方法

类属性的使用方式

#类似属性的使用方式
print(Student.native_pace)
stu1=Student('李四',20)
stu2=Student('王五',22)
print(stu1.native_pace)
print(stu2.native_pace)
#修改类属性
print('-------------------')
Student.native_pace='天津'
print(stu1.native_pace)
print(stu2.native_pace)
吉林
吉林
吉林
-------------------
天津
天津

表明类属性是共享的

类方法的使用方式

print('-----类方法的使用------')
Student.cm()

输出:

-----类方法的使用------
我是类方法,因为我使用了classmethod进行修饰

类方法

@classmethod
def cm(cls):
    print('我是类方法,因为我使用了classmethod进行修饰')

类静态方法的使用

print('-----静态方法的使用------')
Student.method()

输出:

-----静态方法的使用------
我使用了staticmethod进行修饰,所以我是静态方法

静态方法

#静态方法
@staticmethod
def method():
    print('我使用了staticmethod进行修饰,所以我是静态方法')

七、动态绑定属性和方法

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

'''
动态绑定属性和方法
'''
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print(self.name+'在吃饭')

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

print(id(stu1))
print(id(stu2))

一个Student类可以创建N多个Student类的实例对象,每个实体对象的属性值不同

为某个实例对象动态绑定属性

stu1.gender='女'
print(stu1.gender)

输出:女

与java对比,属性可以在对象上动态增加

为实例对象绑定方法

类之外的方法被称为函数,将函数绑定给的对象

'''
动态绑定属性和方法
'''
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print(self.name+'在吃饭')

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

print(id(stu1))
print(id(stu2))

stu1.gender='女'
print(stu1.gender)

#绑定方法
def show():
    print('定义在类之外的,称为函数')

stu1.show=show
stu1.show()

输出:

2243355037648
2243355037552
女
定义在类之外的,称为函数

八、面向对象的三大特征

  • 封装:提高程序的安全性
    • 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行类对象的外部调用方法。这样,无需关系方法内部的具体实现细节,从而降低复杂度。
    • 在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个”_“。
  • 继承:提高代码的复用性
  • 多态:提高程序的可扩展性和可维护性

九、封装

class Car:
    def __init__(self,brand):
        self.brand=brand
    def start(self):
        print('汽车已启动...')

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

输出:

汽车已启动...
宝马

这样就在类之外使用了封装的属性和方法

私有属性

class Student:
    def __init__(self,age):
        self.set_age(age)
    def set_age(self,age):
        if 0<=age<=120:
            self.__age=age
        else:
            self.__age=18
    def get_age(self):
        return self.__age

stu1=Student(150)
stu2=Student(30)
print(stu1.get_age())
print(stu2.get_age())

私有属性

class Student:
    def __init__(self,name,age):
        self.name=name
        self.__age=age #年龄不希望被外部使用,所以加了两个__
    def show(self):
        print(self.name,self.__age)

使用时:

stu1=Student('张三',21)
print(stu1.__age)

报错:

    print(stu1.__age)
AttributeError: 'Student' object has no attribute '__age'
stu1=Student('张三',21)
# print(stu1.age)
stu1.show()

输出:

张三 21

在被隐藏的情况下查看私有属性

print(dir(stu1))

输出:

['_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']

这样就可以看到有哪些属性和方法

没有找到__age属性,但是找到一个 ‘_Student__age’

print(stu1._Student__age)

输出:21

在类的外面依然可以使用,只是要麻烦一点

十、继承

语法格式

class 子类类名(父类1,父类2…) :

​ pass

  • 如果一个类没有继承任何类,则默认继承object
  • Python支持多继承
  • 定义子类时,必须在其构造函数中调用父类的构造函数

定义类:

子类调用父类方法 super()

class 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,teacherofyear):
        super().__init__(name,age)
        self.teacherofyear=teacherofyear

定义实例对象:

stu=Student('张三',20,'1001')
teacher=Teacher('李四',34,10)

调用:info()方法继承自Person类

stu.info()
teacher.info()

输出:

张三 20
李四 34

多继承

class A(object):
    pass

class B(object):
    pass

class C(A,B):
    pass

与Java不同,Java只能单继承,而Python可以搞多继承

方法重写

  • 如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写
  • 子类重写后的方法中可以通过super().xxx() 调用父类中被重写的方法
class 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(self.stu_no)


stu=Student('张三',25,'1001')

stu.info()

子类重写了父类的info()方法

输出:

张三 25
1001

十一、object类

  • object类是所有类的父类,因此所有类都有object类的属性和方法。

  • 内置函数dir()可以查看指定对象的所有属性

  • Object有一个 " __ str __ () " 方法,用于返回一个对于“对象的描述” ,对应于内置函数str()

    经常用于print()方法,帮我们查看对象的信息,所以我们经常会对 __ str __ () 进行重写

    class Student:
        pass
    
    stu=Student()
    print(dir(stu))
    

    查看对象的所有属性

    ['__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__']
    

打印对象

print(stu)

输出:

<__main__.Student object at 0x000001EF126D58E0>

查看对象信息

先重写方法 __ str __ ()

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return '我的名字是{0},今年{1}岁'.format(self.name,self.age)

stu=Student('张三',26)
print(dir(stu))
print(stu)

输出:

['__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']
我的名字是张三,今年26岁

当你重写了 str 方法后,你再打印对象,就不会再打印对象的内存地址,而是调用 str 函数

str 方法经常重写,用于返回对象的描述

查看类型

print(type(stu))
<class '__main__.Student'>

会是创建时的子类类型,而不是object

十二、多态

简单地说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法。

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(obj):
    obj.eat()

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

输出:

猫吃鱼
狗吃骨头
动物会吃
人吃五谷杂粮

这样,就根据传入的对象类型,进行不同的输出

没有继承,也能实现多态,跟Java有区别

静态语言和动态语言

  • 静态语言和动态语言关于多态的区别
    • 静态语言实现多态的三个必要条件
      • 继承
      • 方法重写
      • 父类引用指向子类对象
  • 动态语言的多态崇尚“鸭子类型” 当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不用关心对象时什么类型,到底是不是鸭子,只关心对象的行为。

十三、特殊方法和特殊属性

名称描述
特殊属性__ dict __获得类对象或实例对象所绑定的所有属性和方法的字典
以下为特殊方法__ len __ ()通过重写 __ len __ () 方法,让内置函数len() 的参数可以是自定义类型
__ add __ ()通过重写 __ add __ () 方法,可以使自定义对象具有 “+” 功能
__ new __ ()用于创建对象
__ init __ ()对创建的对象进行初始化

特殊属性

  • __ dict __
class A:
    pass

class B:
    pass

class C(A,B):
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def roar(self):
        print('C is roar')

#创建C类的对象
x=C('Jack',20)
print(x.__dict__) #实例对象的属性字典
print(C.__dict__) #类对象的属性和方法字典   
#该部分代码,后续的几个方法 里 x  C 都指的这里面的

输出:

{'name': 'Jack', 'age': 20}
{'__module__': '__main__', '__init__': <function C.__init__ at 0x00000199020715E0>, 'roar': <function C.roar at 0x00000199022821F0>, '__doc__': None}

__ class __

print(x.__class__) #输出实例对象所属的类

输出:

<class '__main__.C'>

__ bases __

print(C.__bases__) # 输出类的父类的元祖,因为可能不止继承一个,只看上一级

输出:

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

__ base __

print(C.__base__) # 输出类的父类,如果有多个,就只输出第一个,只看上一级

输出:

<class '__main__.A'>

__ mro __

print(C.__mro__) # 输出类的层级关系

输出:

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

__ subclasses __

print(A.__subclasses__()) # 输出类的子类

class D(A):
    pass

print(A.__subclasses__())

输出:

[<class '__main__.C'>, <class '__main__.D'>]

输出的是子类的列表

特殊方法

  • __ add __()方法

    • 让两个对象相加
    a=20
    b=100
    c=a+b
    d=a.__add__(b)
    
    print(c)
    print(d)
    

输出:

120
120

这里相加的都是整数

但是,如果是两个自定义的实例对象呢

class Student:
    def __init__(self,name):
        self.name=name

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

stu1=Student('张三')
stu2=Student('李四')

s= stu1+stu2

报错:

    s= stu1+stu2
TypeError: unsupported operand type(s) for +: 'Student' and 'Student'

但是把 __ add __() 方法放开:

class Student:
    def __init__(self,name):
        self.name=name

    def __add__(self, other):
        return self.name + other.name

stu1=Student('张三')
stu2=Student('李四')

s= stu1+stu2
print(stu1+stu2)
print(stu1.__add__(stu2))

输出:

张三李四
张三李四

在定义过后,就可以相加了

要实现两个对象的加法运算,需要在两个对象的父类编写 __ add __() 特殊方法

__ len __() 方法

输出对象的长度

lst=[22,33,55,66]
print(len(lst))
print(lst.__len__())

输出:

4
4

len(lst) 这里使用的是内置函数,lst. __ len __() 列表对象本来就有这个方法

但是:问题是,自定义的对象没有

上面定义的 stu1 不能使用这个方法,必须自定义

class Student:
    def __init__(self,name):
        self.name=name

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

stu1=Student('张三')
stu2=Student('李四')

print(stu1.__len__())

输出:

2

stu2=Student('李四2')
print(stu2.__len__())

输出:

3

十四、__ new __ 与 __ init __ 演示创建对象的过程

class Person(object):
    def __new__(cls, *args, **kwargs):
        print('__new__被执行了,cls的id值为{0}'.format(id(cls)))
        obj=super().__new__(cls)
        print('创建的对象的id为:{0}'.format(id(obj)))
        return obj
    
    def __init__(self, name, age):
        print('__init__被调用了,self的id值为:{0}'.format(id(self)))
        self.name=name
        self.age=age

print('object这个类对象的id为:{0}'.format(id(object)))
print('Person这个类对象的id为:{0}'.format(id(Person)))

#创建Person类的实例对象
p1=Person('张三',20)
print('p1这个Person类的实例对象的id:{0}'.format(id(p1)))

输出:

object这个类对象的id为:140715602066944
Person这个类对象的id为:3186528480864
__new__被执行了,cls的id值为3186528480864
创建的对象的id为:3186530217744
__init__被调用了,self的id值为:3186530217744
p1这个Person类的实例对象的id:3186530217744

十五、类的浅拷贝与深拷贝

变量的赋值操作

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

class DISK:
    pass

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

#(变量的赋值)
cpu1=CPU()
cpu2=cpu1
print(cpu1,id(cpu1))
print(cpu2,id(cpu2))

输出:

<__main__.CPU object at 0x0000013C8AB45FD0> 1359536742352
<__main__.CPU object at 0x0000013C8AB45FD0> 1359536742352

其实这个过程中,只生成了一个CPU实例对象,然后把cpu1 指向 这个实例对象的地址赋值给了cpu2

也就是说cpu1,cpu2指向的都是同一个对象

浅拷贝

  • Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象和拷贝对象会引用同一个子对象

    class CPU:
        pass
    
    class DISK:
        pass
    
    class Computer:
        def __init__(self,cpu,disk):
            self.cpu=cpu
            self.disk=disk
    
    #(变量的赋值)
    cpu1=CPU()
    cpu2=cpu1
    print(cpu1,id(cpu1))
    print(cpu2,id(cpu2))
    
    #(2)类有浅拷贝
    print('----------------------')
    disk=DISK() #创建一个硬盘类的对象
    computer=Computer(cpu1,disk) # 创建一个计算机类的对象
    
    #浅拷贝
    import copy
    computer2=copy.copy(computer)
    print(computer,computer.cpu,computer.disk)
    print(computer2,computer2.cpu,computer2.disk)
    

输出:

<__main__.CPU object at 0x00000295212D6FD0> 2839530008528
<__main__.CPU object at 0x00000295212D6FD0> 2839530008528
----------------------
<__main__.Computer object at 0x00000295212D6970> <__main__.CPU object at 0x00000295212D6FD0> <__main__.DISK object at 0x00000295212D6F70>
<__main__.Computer object at 0x00000295212D6880> <__main__.CPU object at 0x00000295212D6FD0> <__main__.DISK object at 0x00000295212D6F70>

computer和computer2 指向的CPU和DISK实例对象是一样的,这两个子对象指向是不变的,这样的叫浅拷贝

深拷贝

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

    #深拷贝
    computer3=copy.deepcopy(computer)
    print(computer,computer.cpu,computer.disk)
    print(computer3,computer3.cpu,computer3.disk)
    

输出:

<__main__.Computer object at 0x00000252D49A6970> <__main__.CPU object at 0x00000252D49A6FD0> <__main__.DISK object at 0x00000252D49A6F70>
<__main__.Computer object at 0x00000252D49A6790> <__main__.CPU object at 0x00000252D49A64C0> <__main__.DISK object at 0x00000252D49A64F0>

可以看到,computer深拷贝后,computer3实例对象包含的cpu和disk对象地址都跟computer包含的不一样了,不仅是computer对象拷贝了,连子对象cpu和disk对象也拷贝了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值