04-python中的面向对象编程-02

04-python中的面向对象编程-02

​ 之前 我们谈到面向对象的一些基础知识,今天 我们 继续探讨 python 中的面向对象编程第二篇, 上一篇文章 简单介绍了 类, 对象,以及 类以及对象的关系 ,今天更加详细的介绍 Python语言中 面向对象 的相关细节.

​ 面向对象 一般都会提到 封装 ,继承,多态 这些概念 看起来 比较抽象, 其实之后随着 你慢慢地 使用就会发现 没有那么难.

基础知识

介绍面向对象 之前 先介绍一下 一下,python 中面向对象的编程一些语法知识

前一篇的文章,我们讲了 类 和对象的关系, 什么是类,什么是对象.

类相当于一个模板, 可以用来生成一堆对象. 每个对象的属性值 可以相同也可以不同.

还以之前的代码为例:

class Person:

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

    def cook(self):
        print(f"{self.name} is cooking")

    def eat(self):
        print(f"{self.name} is eating")
        self.weight = self.weight + 2

    def swim(self):
        print(f"{self.name} is  swimming")
        self.weight = self.weight - 1

上面 我定义了Person类, 有一些方法 cook ,eat swim

你可能 对 里面 方法 比较好奇, 这里方法 和普通函数的区别是什么?

实例方法 就是绑定 实例对象的一个函数, 这个函数供 类生成的实例调用.

而普通函数 就是一个函数,没有绑定 任何对象,可以直接调用.

可以看到 每一个方法 第一个参数都有 self 这个参数

    def cook(self):
        print(f"{self.name} is cooking")

以这个方法为例 ,这里self 是什么呢? 你可能会好奇. 这里self 即 将要 生成对象,这里用self 表示.

下面我来实例化 这个类. 当实例化完成之后,p 三个属性被赋值了.

p = Person(name='frank',height=165,weight=55)

p.name == 'frank' 当 对象调用 cook() 方法的时候

p.cook() 相当于是这样 cook(p) , 看到没有 这就是 self 的作用 , self 就表示 当前对象.

假设 又有一个实例

p2 = Person(name='frank2',height=165,weight=50)
p2.name   # frank2

p2.cook() 相当于 cook(p2) 这样调用, 这个时候 你能够看明白了吗?

self 就是 谁调用了这个方法, 谁就是这个self .

类 用来 创建一个个对象,画了一个简单的图,来说明一下, 当 执行 Person(name='frank2',height=165,weight=55) 这个代码的时候,就会 调用 __init__ 方法 来生成 一个对象, 并且 把这些值 为复制 到 这个对象的属性上面。 类相当于一个工厂 用来生产 一批相同的对象,只是对象 初始化的 属性不同,对象的行为(方法) 是完全一样的。

类和对象关系-img-01

你可能有疑问, 为啥是self ,可以使用其他的关键字来替换吗?

当然 可以 其实在其他的语言中就用的 不一样,比如 Java 语言 使用 this 代表当前对象. 这里 self 就是一个名称, 换成其他名字也是可以的, 但是几乎很少人会这样做,因为大家都用self 作为当前的实例, 尽管 你可以换名称, 但并不建议你 这么做. 毕竟 大家都用self 作为当前的实例. (习惯的力量)

实例方法

>>> 
... class Person:
... 
...     def __init__(this, name, height, weight):
...         this.name = name
...         this.height = height
...         this.weight = weight
... 
...     def cook(this):
...         print(f"{this.name} is cooking")
...         
>>> 
>>> p = Person(name='frank',height=165,weight=55)
>>> p.cook()
frank is cooking

类方法

继续 聊聊 下一个 方法 叫类方法, 类方法是什么意思,就是这个方法 属于类进行调用不需要用实例调用,这个方法 所有对象 公用这个方法.

一般形式 需要用一个 装饰器进行修饰 classmethod

这里 我在 Person 类定义一个 类属性, description 这里 来说明 这个类的简要概括.

这个属性 就是 这个类的所有的对象 公有, 这个时候 就要把 这个属性 从 __init__ 方法里面 抽出来,因为 每个对象都是 一样的,就没有必要 在 __init__ 了.

所以 类方法 也是这个意思, 类方法 是希望 这个类来调用这个方法, 所有 由这个类生成的对象,都是可以 使用这个方法的, 并且 这个方法 只会操作 类属性,而不会操作 自身的属性.

这样的方法 就是类方法

class Person:
    # 类属性
    description = 'this is a person'

    def __init__(this, name, height, weight):
        this.name = name
        this.height = height
        this.weight = weight
		
    # 实例方法
    def cook(this):
        print(f"{this.name} is cooking")
		
    
    # 类方法 供类调用
    @classmethod
    def get_description(cls):
        return cls.description
      
      
      
if __name__ == '__main__':
    p = Person(name='frank', height=165, weight=55)
  

上面 get_description 就是一个类方法, 从面向对象的角度来说,这个方法 用来操作 属于类的数据,而不是 对象的数据.

在控制台 里面 演示

>>> 
>>> p = Person(name='frank', height=165, weight=55)
>>> 
>>> p.get_description()
'this is a person'
>>> p2 = Person(name='frank2', height=165, weight=55)
>>> p2.get_description()
'this is a person'
>>> p3 = Person(name='frank3', height=165, weight=55)
>>> Person.get_description()
'this is a person'

可以看出 无论通过 生成一个对象来调用,还是 直接 使用类名 去调用这个方法 都是可以的, 这里返回 都是这个类 的描述.

所以类方法 可以使用类 进行调用,也可以使用对象进行调用. 从上面的例子可以看出来.

静态方法

还有一种方法 叫做 静态方法 ; 静态方法 是什么意思呢?

简单来说 这个方法 本来就是 一个函数,和这个类没有任何关系, 即这个方法 不操作 这个类的属性( 数据),以及对象的属性(数据) , 就是这样, 这样的方法 就是静态方法 .

静态方法 唯一的好处就是 把 这个函数 放在 类的作用域内, 对于 其他的类 这个方法 就不可见了.

看例子:

class Person:
    # 类属性
    description = 'this is a person'

    def __init__(this, name, height, weight):
        this.name = name
        this.height = height
        this.weight = weight

    def cook(this):
        print(f"{this.name} is cooking")

    def eat(self):
        print(f"{self.name} is eating")
        self.weight = self.weight + 2

    def swim(self):
        print(f"{self.name} is  swimming")
        self.weight = self.weight - 1
		
    
    # 类方法 供类调用
    @classmethod
    def get_description(cls):
        return cls.description
	
  	# 静态方法
    @staticmethod
    def add(a, b):
        return a + b

上面 add 方法 ,就是一个静态方法, 为啥呢? 因为这个方法,没有对 这个类的数据,或者对象的数据 进行操作.

所以这个方法就是 静态方法, 这个时候 需要 用staticmethod修饰一下, 才能变成静态方法.

静态方法 如何调用?

静态方法 可以直接使用类名 去调用, 或者使用对象 也是可以调用的.

>>> 
>>> p = Person(name='frank', height=165, weight=55)
... 
>>> 
>>> p.add(10,5)
15
>>> p.add(11,5)
16
>>> Person.add(1,2)
3

所以 静态 方法 就是 相当于一个函数, 而只是 这个函数 写在 class 里面了而已, 所以 类是可以调用这个方法,同时 对象也是可以调用这个方法的.

方法类型 对象
类方法 可以调用 可以调用
静态方法 可以调用 可以调用
实例方法 不可以调用 可以调用

简单总结一下 , 在python 中 定义一个类,可以有很很多类型的方法 ,类方法,实例方法,静态方法 。 要知道 每种方法 需要如何定义,以及每种方法的特点。

类方法 的一些要走的坑

# -*- coding: utf-8 -*- 

class A:
    name = 'A'

    @classmethod
    def get_cls_name(cls):
        return cls.name


if __name__ == '__main__':
    a1 = A()
    a2 = A()
    a3 = A()

    print(a1.get_cls_name())
    print(a2.get_cls_name())
    print(a3.get_cls_name())

    print("修改 a2.name")
    a2.name = 'a2'
    print(a2.get_cls_name())  # A
    print(a2.name)    # a2
    pass

我尝试 修改 a2.name 的值 , 然后获取值 发现 类A 中的name 并没有修改。

原因是什么呢? 其实 这源于 python 语言的动态性。

当执行 a2.name = ‘a2’ 然后 会在 a2 对象生成一个属性name .

让我们在Console 看下

... class A:
...     name = 'A'
... 
...     @classmethod
...     def get_cls_name(cls):
...         return cls.name
... 
>>> 
>>> 
>>> a1 = A()
... a2 = A()
... a3 = A()
>>> 
>>> 
>>> a1.get_cls_name()
'A'
>>> a2.get_cls_name()
'A'
>>> a3.get_cls_name()
'A'
>>> print("修改 a2.name")
... a2.name = 'a2'
修改 a2.name
>>> a2.name
'a2'
>>> a2.get_cls_name()
'A'

>>> a1.name
'A'
>>> a3.name
'A'




>>> pprint(dir(a2))
['__class__',
 	...
 '__weakref__',
 'get_cls_name',
 'name']


>>> pprint(dir(A))
['__class__',
 ...
 '__weakref__',
 'get_cls_name',
 'name']

>>> # 看下id 值
>>> id(A.name)
1886933580016
>>> id(a1.name)
1886933580016
>>> id(a2.name)
1886948838640

通过 dir 函数 去查看 a2,A 的属性 发现 都有 name ,get_cls_name 这两个属性 。

然后查看 发现 a2.name 和 A.name 的id 值 是否一样, 发现 是不一样的。

a2.name = 'a2' 会在对象上面添加一个属性name 这个属性绑定在实例a2 上面了。 不会影响 a1, a3 ,通过 打印 id 值 ,就可以发现 A.name 与 a1.name 的id值是相同的。

所以这里简单 总结一下 ,不要想通过 实例对象 来修改类变量的值, 因为 这样只会重新一个属性 绑定在 这个对象上面 。

这个原因是python语言的动态性。

总结

这篇文章 介绍了python 语言中 面向对象编程的基础知识, 常见的几种方法 . 比较简单吧, 之后 我会继续 写这个专题,继续 介绍 面向对象 的几个特点 封装 ,继承 ,多态等 基本特性. 加油,好好学习.

分享快乐,留住感动. 2020-06-11 20:40:19 --frank
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章