面向对象

面向对象


面向对象的简介

对象

我的另外一个blog有详细的讲什么是对象,还有可变对象和不可变对象,这里不多做赘述,详情可以点击链接查看

可变对象与不可变对象

面向对象

提到面向对象,不得不提提面向过程,可以说面向对象是由面向过程发展而来的

面向对象和面向过程
这个也是我的blog,有讲到面向过程和面向对象

这里我再说一说

面向过程和面向对象首先都是编程的思想

面向过程关注的是解决问题的步骤,通过将问题的解决分为一个一个的步骤来进行编程,这样的编程更加符合我们人类的思维过程,但这样往往只适用于一种功能,复用性低

面向对象关注的对象,至于该对象是如何解决问题的,解决问题的步骤则不关注,这样的方式不太容易阅读,而且不符合我们的思维过程,编写起来比较困难;但它的好处是复用性高,易于维护


类的快速入门

  • 类首先是一个对象,是用来创建对象的对象,类型为type
  • 类和对象的关系:类相当于一张建筑图纸,对象则相当于该图纸上一个个成型的建筑。所以我们称对象为类的实例(instance)
  • 我们可以用isinstance来判断该对象是否是类的实例
# a = 123
# a = int(123)
# f = isinstance(a,int)
# print(a,f)
# int也是一个类,同理float,list,bool...都是类

像int(),list()等都是Python内置的类。通常这些内置的类不能够满足我们的需求

所以我们用关键字class来自定义类

# class MyClass:
#     pass
# print(MyClass)  # <class '__main__.Myclass'>
# print(id(MyClass),type(MyClass))  # 2025575315856 <class 'type'>
# 类的对象是type类型

# mc = MyClass()  # mc是MyClass创建的一个实例对象
# print(mc,type(mc))  # <__main__.MyClass object at 0x000001D79F927670> <class '__main__.MyClass'>
# mc的类型是MyClass
# 根据mc和MyClass的id可以看出两个是不同的对象

我们可以向对象中添加变量,对象中的变量我们称之为属性。

语法:对象.属性名 = 属性值

# mc.name = 'Sleet'
# print(mc.name)

类的定义

对象都是现实生活中的事物的抽象

现实生活中的事物其实由两部分组成:

  • 数据(属性)
  • 行为(方法)
# 定义一个人的类
# class Person:
#
#     name = 'Sleet'  # 在类中定义的变量,所有实例都可以进行访问(公共属性)
#     age = 19

    # 在类中的一的函数,我们称之为方法,这些方法所有实例都可以访问
    # 在类中定义的函数在调用时默认传递一个参数,所以在定义时也至少要传递一个形参
    # def speak(self):
    #     print('加油呀~')

# p1 = Person()

# print(p1.name)
# p1.speak()

属性和方法的查找流程

当我们调用对象的属性或者方法时,解析器首先会在当前对象中查找,如果有则直接返回值,如果没有就在创建该对象的类中查找,如果有则直接返回值,如果没有就报错

类和对象中都可以保存属性,通常如果该属性是所有实例共有的就在类中定义,如果是某个实例私有的,就在对象中定义


self参数

前面说到在类中定义的函数都至少要定义一个形参,因为在调用时会默认传递一个参数

当我们打印这个参数时发现,返回的地址和调用该函数的对象本身的返回的地址是一样的。

这说明对象在调用函数时默认传递的参数其实就是对象本身

所以我们习惯把这个参数命名为self

# class Person:
# 
#     def speak(self):
#         print('加油~',self.name)
#         # print(a)
# p1 = Person()
# p1.name = '蜘蛛侠'
# p1.speak()
# print(p1)
# <__main__.Person object at 0x000001FB66578430>
# <__main__.Person object at 0x000001FB66578430>
# 说明在调用类里面的函数默认传递的那个参数就是调用者本身(即p1调用,传递的参数是p1本身)
# 所以我们习惯把这个默认传递的参数命名为self

# p2 = Person()
# p2.name = '蝙蝠侠'
# p2.speak()

类的特殊方法

  • 在类中我们可以定义一些特殊的方法
  • 这些方法的名称形如:__ 方法名 __,(双下划线中间一个方法名)
  • 这些特殊方法不需要我们自己调用,它们会在特定的时候自动调用
1. __ init __
# class Person:
#
#     print('Person代码块中的代码')
#
#     def __init__(self,name):
#         print('晚上好')
#         # 通过self向新创建的对象来初始化属性
#         self.name = name
#     print('Person代码块中的代码')

# p1 = Person('钢铁侠')

输出的结果为

Person代码块中的代码
Person代码块中的代码
晚上好

根据结果可以看出

  1. 当定义类之后,会执行类里面的语句,所以两个print语句先打印了出来
  2. 当对象被创建之后,__ init __ 函数会自动调用,所以输出了“晚上好”

封装

封装的引入

首先我们定义一个类

# 定义一个车类
# 属性 name color 方法:run(), laba()

# class Car():
#
#     def __init__(self,name,color):
#         self.name = name
#         self.color = color
#
#     def run(self):
#         print('%s的%s车开始跑了'%(self.color,self.name))
#
#     def laba(self):
#         print('dididi')

当我们创建该类的实例,传递车的名字和颜色的时候会发现,我们可以传递任意值,这样会导致如果我们输入的不是车,它也能执行车的功能。这样的方式会让我们的属性非常不安全。因此我们需要让我们的数据更加安全:

  1. 属性不能随意修改
  2. 属性不能修改成任何值

所以,我们需要对数据进行封装

封装

封装是面向对象的三大特性之一

封装是指隐藏对象中一些不希望被外部访问到的属性和方法

那如何隐藏一个属性

一、将属性名修改为非常规值

# class Dog:
#
#     def __init__(self,name,age):
#         self.hidden_name = name
#         self.hidden_age = age

将我们常用的name修改成hidden_name,这样其实也能够随意访问和修改,只要将访问的name也改成hidden_name即可。但这能起到一定的提醒作用,也就是说告诉别人,这是一个希望隐藏的值。

二、在属性名前面加上双下划线(__属性名)

# class Person:
#
#     def __init__(self,name):
#         self.__name = name

这样定义的属性名,在外部就访问不到了,当然也修改不了

但其实这样在属性名加上双下划线之后,Python会自动修改这个属性名,改成 _类名__属性名 。访问“ _类名__属性名 ”同样也能访问到和修改,只不过Python不建议这样做

三、在属性名前加上单下划线(_属性名)

# class Person:
#
#     def __init__(self,name):
#         self._name = name

这样定义的属性名,和第一种情况一样,只能够起到提醒的作用,告诉别人这是一个封装的属性,在外部同样能够访问和修改

对封装属性的访问和修改

那么既然属性被封装起来了,如果想要访问和修改要怎么做呢

访问

需要在类中定义一个getter函数,getter只是一个名称,并非特殊函数

#     def get_name(self):
#         # get_name用来获取对象中的name属性
#         return self.__name

修改

需要在类中定义一个setter函数来设置,setter同样只是一个名称,并非特殊函数

#     def set_name(self,name):
#         # set_name用来设置对象中的name属性
#         self.__name = name

如果定义的类中有这两个函数,说明封装起来的属性允许被修改,如果没有这两个函数,说明封装起来的属性不希望被修改,这时候就不要去修改了


property装饰器

@property 用来创建只读属性,@property 装饰器会将方法转换成相同名称的只读属性

# class Person:
#
#     def __init__(self,name):
#         self._name = name
#
#     @property
#     def name(self):
#         print('get方法执行了')
#         return self._name
#
#     # setter方法的装饰器 @属性名.setter
#     @name.setter
#     def name(self,name):
#         print('setter方法执行了')
#         self._name = name
#
# p = Person('葫芦娃')
# p.name = '超人'
# print(p.name())  # p.name() ---> 葫芦娃
# print(p.name)  # name是一个方法

本来name是一个方法,在创建的对象进行调用时,需要在后面加上括号"()",但通过@property装饰了该函数时,直接调用函数名name就可以了。这样更符合我们的思维过程。


继承

继承也是面向对象的特性之一

  • 在类后面的括号中可以指定父类
  • 通过继承可以获取父类的属性和方法
  • 增加了类的复用性,使类与类之间产生了关系
super()

super()可以获取当前类的父类,通过super()返回的对象在调用父类方法时不需要传递self参数

class Person:

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

    def Speak(self):
        print('我会说话')

    def Walk(self):
        print('我能走路')

class Student(Person):

    def __init__(self,name,age,sex):
        # super()获取当前类的父类,初始化父类的name,age和sex属性
        super().__init__(name,age,sex)

    def Study(self):
        print('我需要学习')

s = Student('张同学','16','男')
s.Speak()
# 子类继承了父类的两个方法
s.Walk()
s.Study()
方法的重写

如果子类中存在与父类同名的方法,通过实例对象调用这个方法时,会调用子类的方法而非父类的方法。这个特点我们称之为方法的重写(覆盖)

方法的查找流程:

  1. 现在当前类中查找,如果没有,则去当前类的父类中查找
  2. 如果当前类的父类中没有,就去当前类的父类的父类中查找,以此类推,直到查找到
  3. 如果到最后都没有找到,则会报错
class A:

    def test(self):
        print('A')

class B(A):
    def test(self):
        print('B')

class C(B):
    pass

c = C()
c.test()
多重继承

一个类可以在括号中指定多个父类,这就叫多重继承,这个子类可以获取所有父类中的方法。当几个父类中存在同名的方法时,会按从左到右的顺序进行查找,直到没有找到报错。

class A:

    def test(self):
        print('A')

class B:

    def test(self):
        print('B')

# class C(B,A):
#     pass

# class C(A,B):
#     pass

# c = C()
# c.test()

多态

多态也是面向对象的特性之一,从字面上理解就是多种形态

一个对象可以有多种形态进行呈现,这就是多态

class A:

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

    @property
    def get_name(self):
        return self._name

    @get_name.setter
    def set_name(self,name):
        self._name = name

class B:

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

    @property
    def get_name(self):
        return self._name

    @get_name.setter
    def set_name(self,name):
        self._name = name

a = A('Sleet')
b = B('Jerry')

# 定义一个函数,这种形式就是一个多态,obj可以有多种形态
def speak(obj):
    print(obj.get_name,'很厉害')

# 这种形式就违反了多态的原则,限制了对象
def speak2(obj):
    if isinstance(obj,A):
        print(obj.get_name)

speak(a)
speak(b)

面向对象的三大特性

  1. 封装: 确保对象中数据的安全
  2. 继承: 保证了对象的扩展性
  3. 多态: 保证了程序的灵活性
属性和方法
属性
  • 类属性:在类中直接定义的属性是类属性,类属性能够通过类和实例对象进行访问,但只能通过类进行修改
  • 实例属性:在实例对象中定义的属性是实例属性,实例属性只能够通过实例对象进行访问和修改
方法
  • 实例方法:默认传递的参数是self,实例方法可以通过类和实例对象进行调用,但通过类进行调用时需要手动传递参数self
  • 类方法:用@classmethod装饰器装饰的方法,默认传递的参数是cls,能够通过类和实例进行调用
  • 静态方法:用@staticmethod装饰器装饰的方法,不需要传递参数,是一种工具方法,与当前类无关
class Person:

    # 在类中定义的属性是类属性
    # 类属性能够通过类和实例对象进行访问,但只能通过类进行修改
    character = 'handsome'

    def __init__(self,name,age,sex):
        self._name = name
        self._age = age
        self._sex = sex

    # 以self为参数传递的是实例方法
    # 实例方法可以通过类和实例对象进行访问,不过通过类进行访问时需要传递self参数
    def test1(self):
        print('这是实例方法')

    # 通过@classmethod装饰器装饰的方法是类方法
    # 类方法能够通过类和实例对象进行访问
    # 类方法默认传递的参数是cls
    @classmethod
    def test2(cls):
        print('这是类方法')

    # 静态方法是用@staticmethod装饰器装饰的方法
    # 静态方法不需要传递默认的参数
    # 静态方法只是一种工具,与当前类无关
    @staticmethod
    def test3():
        print('这是静态方法')

c = Person('a',12,'nan')
# 实例属性,通过实例对象添加的属性是实例属性
# 实例属性只能通过实例对象进行访问和修改
c.character = 'ugly'
print(Person.character)
print(c.character)
# handsome
# ugly
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值