06-1 | 面向对象核心概念

逸兴
逸兴
逸兴
57
文章
25
评论
2020-04-2507:51:35
评论
5026字阅读16分45秒

什么是面向对象?

面向对象是一种方法论,一种认识世界,分析世界的方法论。可以将万事万物抽象为各种对象,就是一个个的个体。

而各种对象具有的相同的特征,称为。这些对象可以称之为一类对象,一类个体。

在计算机中“”是“属性”和“方法”的集合。

比如:

狗咬人

我们把狗抽象为 狗类,这个狗类中包含了许多对象,它们都有相同的特征-都是狗。

把人抽象为 人类,这个人类中包含了许多对象,它们都有相同的特征-都是人。

咬人的这只狗是一个具体的对象,而狗类是一个抽象的概念。被咬的那个人,也是一个具体的对象,是一个个体。

我们用“狗类”可以描述所有狗,因为他们有相同的特征“都是狗”,这里的都是狗,可以看做这个类的属性。它们都会“睡觉”,“吃饭”,“咬人”这些便是它们的动作或者叫做方法。

狗类和人类本来没有关系,但是因为这个“咬人”的动作,狗和人就又联系到了一起。

面向对象3要素

1.封装

封装就是定义类。

函数的封装是为了“复用”,类的封装也是为了“复用”。另外也可以隐藏内部细节。

封装就是将“属性”和“动作”组装到一起,形成一个“类”。

只对外暴露一些接口,通过接口访问对象。

2.继承

子类可以从父类身上继承“属性”,“动作”。子类就不需要再定义了,就具有了,可以减少代码冗余。

比如:

人类有“身高,体重”可以“吃饭,睡觉”,这些是人类的属性和动作,人类实例化的一个个的对象,具体的人,同样继承了这些属性和动作。这样每次创建一个人,就不要在为他单独设置这些属性,因为他们已经继承到了。

同样的“人类”的这些属性,可以是继承于“动物类”,“生物类”。

比如:孩子他的遗传特征继承于他的父亲和母亲,这种称为多继承。

动物---哺乳类动物---灵长类动物----人类 这种是为单一继承。

  • 多复用, 子类会继承父类的特征,子类就不需要单独设置了
  • 多继承少修改
    • OCP原则:如果要修改某个类的属性,应该先继承,在修改。

3.多态

动态绑定

类 class 定义

Python 类定义:

class ClassName:
    语句块

------------------------

# encoding = utf-8
__author__ = "hugbg.com"

class Person:
    """class Person ,定义人类"""
    x = 100     # 定义类属性

    def eat(self):
        """
        类动作,吃, 类方法也是类属性
        :return:
        """
        print('abc')


print(Person)       # 类标识符
print(Person.__name__)      # 类名字
print(Person.__doc__)       # 类文档
print(Person.eat)           # 类动作
print('----------------')
print(Person.eat())
  • class 关键字定义
  • 大驼峰写法,本质上是一个“标识符”
  • 类定义完成后,就产生了一个“类对象”,绑定到了标识符“ClassName”上。

类及类属性

  • 类对象: 类也是对象,类的定义执行后会生成一个类对象
  • 类属性: 类中定义的方法,和变量都是类属性。实例化后的对象,也会具有类属性
  • 类变量: 属性也是标识符,也是变量。

Person中,x,eat 都是类的属性。__doc__, __name__ 是每个类都具有的特殊属性。

x 是类的属性,是这个类所具有的属性,只有这个类的实例才可以调用。

eat 是类的方法,本质上就是一个函数,它一般要求至少有一个参数,第一个形参通常是self(self是一个惯用标识符,可以换名字),这个self 代指当前实例本身。

实例化

a = Person()    # 实例化
实例化可以创建一个真正的该类的对象。
因为同一个类,实例化后的每一个对象都是不同的,可以为每一个实例化对象,传递参数,也就是设置属性。
# encoding = utf-8
__author__ = "hugbg.com"

class Person:
    """class Person ,定义人类"""
    
    # __new__(self)  实例化一个对象 

    def __init__(self, name, age=18):   # 初始化函数
        self.name = name
        self.age = age

    def showage(self):      # 方法,方法中可以调用 初始化中的属性
        return "{} is {}".format(self.name, self.age)
        # 这里调用的 self.name, self.age 就是前面初始化时绑定的这个实例的name属性和age属性。


tom = Person('Tom', 21)    # 实例化一个tom
jerry = Person('Jerry')    # 实例化一个jerry

print(tom.name, tom.age)    # 打印 tom的属性
print(jerry.name, jerry.age)

print(tom.showage())    # 调用tom的方法
print(jerry.showage())
06-1 | 面向对象核心概念

python实例化后会调用__init__(self) 方法,进行初始化设置,可以不定义,如果不定义会在实例化后隐藏调用其父类的。

接收到Persion('Tom', 21) 后,__init__(self), 会进行一个实例的初始化,self此时就是 Persion('Tom', 21),__init__ 创建相应的属性self.name, self.age,返回一个实例对象,这个实例对象便被 “等号右边的 tom” 接收了。

其实在python 实例对象时,会先调用__new__(self) 方法,来创建一个对象,这个方法也可以进行自定义,这个方法完成后 self 就存在了,然后给__init__(self) 进行初始化配置。

__init__(self) 不能有返回值,只能return None

其实整个面向对象过程中,最重要的就是 self 代指当前实例本身。在所有的面向对象语言中都是这么做的,不过有的是 this 或者直接隐藏的self。

所以, self.name, self.age 又称为实例属性。它们是保存在实例中,而不是类中。实例属性可以修改添加。

方法绑定概念

类中调用实例的方法时,该方法会绑定到该实例,这样就不用传入self。

比如上面的 tom.showage() 调用时,实例tom调用类方法showage 时,会自动进行方法绑定。就是tom 会第一个注入到showage方法中,此时在showage中 tom就是self。self代指当前实例本身。

tom.showage 其实也会绑定

06-1 | 面向对象核心概念

没有绑定,就没有注入。只有实例调用方法时,才会绑定。类调用类方法,不会绑定,就没有注入。

比如:

06-1 | 面向对象核心概念
06-1 | 面向对象核心概念

self是怎么存在的?

我们知道self代指实例本身,那么它到底是以什么形式存在的呢?

通过一个简单的例子,看下self是什么:

06-1 | 面向对象核心概念

__init__() 中的self 其实就是实例化后的tom,调用方法是 传入的self也是实例本身tom。

self 代指实例本身。

在上面的例子中,tom.showage 会把类方法showage与实例tom绑定起来。

使用tom.showage() 则会把实例本身注入到showage方法中,也就是self。

实例变量和类变量

# encoding = utf-8
__author__ = "hugbg.com"


class Person:
    age = 20

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

    def showage(self):
        print(self.name, 'is',self.age)


tom = Person('Tom')

print(Person.age)       # 类变量 - 20
tom.showage()       # 实例变量未定义时,会使用类变量 - 20
print(tom.name, tom.age)    # - 20
print()

# 动态增加一个实例属性
tom.age += 5    # 此时相当于 定义了一个 实例变量, tom.age = 25, Person.age = 20

print('tom', tom.age)       # 输出的是实例变量的值 - 25
print('Person', Person.age)     # 类变量没有更改还是 20

tom.showage()   # 调用的tom的age
print('-' * 30)


Person.age = 17     # 修改类变量为 17

jerry = Person('Jerry')     # 新实例一个对象
print(Person.age)       # 类变量已经是 17
print(jerry.name, 'is', jerry.age)      # jerry.age 没有定义,使用的是Person.age - 17

print(tom.age)      # tom.age 依然是前面定义的 25
06-1 | 面向对象核心概念

通过实例可以访问类属性

另外一种访问方法:

通过实例访问类属性 __class__

通过类无法访问实例属性

类的特殊属性

特殊属性含义
__name__对象名
__class__实例类对象
__dirt__对象的属性的字典
__qualname__类的限定名

特殊类属性 __class__

06-1 | 面向对象核心概念

特殊类属性 __dirt__

类字典,保存公共的属性,方法

# encoding = utf-8
__author__ = "hugbg.com"

class Person:
    x = 100
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

    def showage(self):
        return "{} is {}".format(self.name, self.age)

print(*Person.__dict__.items(), sep='\n')   # 类字典 __dict__
print('-' * 30)
06-1 | 面向对象核心概念

实例字典,保存实例独有的属性

tom = Person('Tom', 20)
print(*tom.__dict__.items(), sep='\n')      # 实例字典
06-1 | 面向对象核心概念

每个实例都会有一个单独的字典记录属性,所以各个实例的属性是不同的。我们访问的实例属性,其实就是在访问实例字典。

但是需要注意的是:

06-1 | 面向对象核心概念

实例属性的查找顺序

实例使用 . 来访问属性,会现在__dict__中查找,如果没有,则通过__class__找到自己的类,然后再去类的__dict__中查找。

此时访问到了类属性后,也只是访问到了,并不会在自己的__dict__中添加属性。自由定义时候,才会添加。

练习

class Person:
    age = 3
    height = 170

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

tom = Person('Tom') # 实例化、初始化
jerry = Person('Jerry', 20)
Person.age = 30

print(1, Person.age, tom.age, jerry.age) # 输出什么结果 30 18 20

print(2, Person.height, tom.height, jerry.height) # 输出什么结果 170 170 170

jerry.height = 175
print(3, Person.height, tom.height, jerry.height) # 输出什么结果 170 170 175

tom.height += 10
print(4, Person.height, tom.height, jerry.height) # 输出什么结果 170 180 175

Person.height += 15
print(5, Person.height, tom.height, jerry.height) # 输出什么结果 185 180 175

Person.weight = 70
print(6, Person.weight, tom.weight, jerry.weight) # 输出什么结果 70 70 70

print(7, tom.__dict__['height']) # 可以吗 180
print(8, tom.__dict__['weight']) # 可以吗 error
06-1 | 面向对象核心概念

类变量可以使用全大写

类方法 classmethod

classmethod是内建函数之一,作为类方法可以使用实例和类访问,都有绑定,绑定了当前类本身

调用方法时,会自动注入第一个参数,self 就是 class。

classmethod 方法会把类本身(type,__class__)作为第一个参数,注入到被装饰的函数中去。

如下:

06-1 | 面向对象核心概念

使用classmethod装饰时,self 就是 class, 如下:

06-1 | 面向对象核心概念

静态方法 staticmethod:

@staticmethod

与classmethod相反,它不会进行任何绑定,也就是没有任何注入

06-1 | 面向对象核心概念

可以手动传参:

06-1 | 面向对象核心概念




https://www.hugbg.com/archives/2401.html
逸兴
  • 本文由 发表于 2020-04-2507:51:35
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
01-1 数据类型 基础语法

01-1 数据类型

第一章 数据类型 使用type() 函数可以查看数据类型 1.1 字符串 str 字符串是使用单引号或双引号括起来的任意文本。 比如'abc', '123'等 字符串类型 字符串类型用str表示 st...
09-5 | asyncio基本使用 并发编程

09-5 | asyncio基本使用

第一节 关于asyncio asyncio 在3.4 版本中加入到标准库, asyncio基于selector实现, 看似库, 其实是个框架, 包含异步IO, 事件循环, 协程, 任务等内容。 通过a...
09-4 | 全局解释器锁 & 多进程 & 池 并发编程

09-4 | 全局解释器锁 & 多进程 & 池

GIL CPython 在解释器进程级别有一把锁,叫做GIL,即全局解释器锁。 GIL 保证CPython进程中,只有一个线程执行字节码。甚至是在多核CPU的情况下,也只允许同时只能 有一个CPU核心...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: