lesson 040 —— 面向对象简介

lesson 040 —— 面向对象简介

关于面向对象的简单介绍。

三大编程范式

  1. 面向过程编程
  2. 函数式编程
  3. 面向对象编程

面向对象概念

  • 类: 把一类事物相同的特征和动作整合到一起就是类。类是一个抽象的概念。类是一种数据结构。
  • 对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起;对象就是类的实例化,具体化。对象是一个具体的事物。
  • 类与对象的关系: 对象是由类产生的,对象是类的实例化,具体化。
  • 实例化: 由类产生对象的过程叫实例化,类实例化的结果就是一个对象,或者叫一个实例(实例=对象)。
1. 声明类
class 类名:
    """类的文档字符串"""
    类体
    
# 声明一个类,类名一般首字母大写,使用驼峰命名法
# 代表的是:class Data(object):
class Data:
    """这是一个时间的类。"""
    pass

# 实例化这个类
d1 = Data()

在 Python3 中使用新式类,所有的类都继承自一个父类:object。

2. 属性

类是用来描述一类事物的,类的对象指的是一类事物中的一个个体。事物的属性分为:

  1. 数据属性: 就是变量
  2. 函数(方法)属性: 就是函数,在面向对象里称为方法。

注意:类和对象都使用点 . 来访问自己的属性。

3. 类的属性
  1. 类的数据属性: 类的数据属性是所有对象共享的

  2. 类的方法属性: 类的函数属性是绑定给对象用的

  3. 一些内置的类的特殊属性

    类名.__name__# 类的名字(字符串)
    类名.__doc__# 类的文档(字符串)
    类名.__base__# 类的第一个父类(在讲继承时会讲)
    类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
    类名.__dict__# 类的字典属性
    类名.__module__# 类定义所在的模块
    类名.__class__# 实例对应的类(仅新式类中)
    

    使用 dir(类名) 可以查看类的属性!!!

class Data:
    """关于时间的类"""
    
    # 类的数据属性
    year = 2018
    month = 7
    day = 35
    week = '星期三'
    
    # 类的方法属性
    def print_year():
        print('2018 年')
    # 需要传入参数和这个类绑定,不传也没问题,可是不会绑定
    # 可是要使用参数的时候,就需要传入 self
    def print_data(self):
        print('%4d 年 %2d 月 %2d 日' % (self.year, self.month, self.day))

if __name__ == '__main__':
    d1 = Data()
    d2 = Data()

    print("类的属性")
    print("Data.year: %d" % Data.year)
    print("Data.print_year: ")
    Data.print_year()
    # 这里类调用的话,就需要传入一个参数
    print("Data.print_data: ")
    Data.print_data(Data)
    print()
    
    print("类的属性是所有对象公有的")
    print("id Data.year: %d" % id(Data.year))
    print("id d1.year: %d" % id(d1.year))
    print("id d2.year: %d" % id(d2.year))
    print()
    
    print("函数属性是对于不同对象的")
    print("addr Data.print_data: ", Data.print_data)
    print("addr d1.print_data: ", d1.print_data)
    print("addr d2.print_data: ", d2.print_data)
    print("id Data.print_data: %d" % id(Data.print_data))
    print("id d1.print_data: %d" % id(d1.print_data))
    print("id d2.print_data: %d" % id(d2.print_data))
    print()

    print("d1 对象与类属性关系")
    print("d1.year: %d" % d1.year)
    # 这里不能调用,因为类里面是没有参数的,对象调用的话就会传入对象,即 self 参数
    #print("d1.print_year: ")
    #d1.print_year()
    print("d1.print_data: ")
    d1.print_data()
    print()

    print("类改变数据属性:")
    print("Data.year: %d" % Data.year)
    print("d1.year: %d" % d1.year)
    print("d2.year: %d" % d2.year)
    print("执行 Data.year = Data.year + 1")
    Data.year = Data.year + 1
    print("Data.year: %d" % Data.year)
    print("d1.year: %d" % d1.year)
    print("d2.year: %d" % d2.year)
    print()

    print("对象改变数据属性:")
    print("Data.year: %d" % Data.year)
    print("d1.year: %d" % d1.year)
    print("d2.year: %d" % d2.year)
    print("执行 d1.year = d1.year + 1")
    d1.year = d1.year + 1
    print("Data.year: %d" % Data.year)
    print("d1.year: %d" % d1.year)
    print("d2.year: %d" % d2.year)
    print("执行 d2.year = d2.year + 1")
    d2.year = d2.year + 1
    print("Data.year: %d" % Data.year)
    print("d1.year: %d" % d1.year)
    print("d2.year: %d" % d2.year)
    print()

    print("改变之后的地址")
    print("id Data.year: %d" % id(Data.year))
    print("id d1.year: %d" % id(d1.year))
    print("id d2.year: %d" % id(d2.year))
    print()

结果:

类的属性
Data.year: 2018
Data.print_year: 
2018 年
Data.print_data: 
2018 年  7 月 35 日

类的属性是所有对象公有的
id Data.year: 139849464558928
id d1.year: 139849464558928
id d2.year: 139849464558928

函数属性是对于不同对象的
addr Data.print_data:  
addr d1.print_data:  >
addr d2.print_data:  >
id Data.print_data: 139849464102288
id d1.print_data: 139849465338376
id d2.print_data: 139849465338376
    
d1 对象与类属性关系
d1.year: 2018
d1.print_data: 
2018 年  7 月 35 日

类改变数据属性:
Data.year: 2018
d1.year: 2018
d2.year: 2018
执行 Data.year = Data.year + 1
Data.year: 2019
d1.year: 2019
d2.year: 2019

对象改变数据属性:
Data.year: 2019
d1.year: 2019
d2.year: 2019
执行 d1.year = d1.year + 1
Data.year: 2019
d1.year: 2020
d2.year: 2019
执行 d2.year = d2.year + 1
Data.year: 2019
d1.year: 2020
d2.year: 2020

改变之后的地址
id Data.year: 139849465104656
id d1.year: 139849464558832
id d2.year: 139849464558896

从以上实例中我们可以验证得到这几个结论:

  1. 类的数据属性是所有对象共享的,我们称为类的数据

    id Data.year: 139849464558928
    id d1.year: 139849464558928
    id d2.year: 139849464558928
    
  2. 当使用改变类的数据之后,我们发现,所有的对象数据都改变了,即他们的地址都是一样的。

    类改变数据属性:
    Data.year: 2018
    d1.year: 2018
    d2.year: 2018
    执行 Data.year = Data.year + 1
    Data.year: 2019
    d1.year: 2019
    d2.year: 2019
    
  3. 但是,我们使用对象改变类的属性之后,仅仅是这个对象的数据变了,而其它对象和类的都不会改变。我们可以得知:Python 中,直接改变对象中类的数据,其实是创建了新的对象数据,而不是改变了类的数据,即将指向类的数据的变量指向了一个新的数据地址!!!

    对象改变数据属性:
    Data.year: 2019
    d1.year: 2019
    d2.year: 2019
    执行 d1.year = d1.year + 1
    Data.year: 2019
    d1.year: 2020
    d2.year: 2019
    执行 d2.year = d2.year + 1
    Data.year: 2019
    d1.year: 2020
    d2.year: 2020
        
    id Data.year: 139849465104656
    id d1.year: 139849464558832
    id d2.year: 139849464558896
    

    注意:Python 中几乎所有变量的赋值,都是将新的数据地址赋值给这个变量,即这个变量指向了一个新的内存地址,而不是改变变量指向的对应内存地址的数据!!!

  4. obj.name 会先从 obj 自己的名称空间里找 name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常.

  5. 类中的函数属性需要一个 self 形参来和这个类的对象绑定。在 Python 中,self 代表的就是对象,定义的函数属性本质上是定义一个函数。(虽然不加 self 程序不会报错,但是不会和对象绑定)。虽然绑定了,但是当类调用的时候可以随便传入参数,只要传入的参数使函数可以运行。

    def print_data(self):
        print('%4d 年 %2d 月 %2d 日' % (self.year, self.month, self.day))
    
    Data.print_data(Data)
    
  6. 类的函数属性没有 self 参数的时候,只有类可以调用,对象是无法调用的,它是类的方法

    # 这里不能调用,因为类里面是没有参数的,对象调用的话就会传入对象,即 self 参数
    #print("d1.print_year: ")
    #d1.print_year()
    
  7. 通过类来创建对象的时候,对于绑定的类的函数,即有 self 形参的函数,实际上是将那个函数复制到了对象的空间中,有了自己的内存,与类中的地址不一样。所以,通过类创建对象,虽然是同一个类,同一个类的函数,但是,每个对象都会复制函数,每个对象有他们自己的存储这个函数的内存,即变成了对象自己的函数,而不是类的函数。虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法!!!

    addr Data.print_data:  
    addr d1.print_data:  >
    addr d2.print_data:  >
    id Data.print_data: 139849464102288
    id d1.print_data: 139849465338376
    id d2.print_data: 139849465338376
    
  8. 强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将本身当做第一个参数传给方法,即自动传值(方法 __init__ 也是一样的道理)

    注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数 selfself 可以是任意名字,但是约定俗成地写出self。

    # 类调用
    Data.print_data(Data)
    
    # 对象调用
    d1.print_data()
    
4. 对象

对象是类的实例化,类实例化的一个结果称为一个实例或者一个对象。

class Student:
    school='xxoo'
    def __init__(self, name, sex, age):
        print('开始创建')
        self.name = name
        self.age = age
        self.sex = sex
        print('创建结束')
    def learn(self):
        print('%s is learning' % self.name) #新增self.name

    def eat(self):
        print('%s is eating' % self.name)

    def sleep(self):
        print('%s is sleeping' % self.name)

# 创建对象
s1=Student('李坦克','男',18)
s2=Student('王大炮','女',38)
s3=Student('牛榴弹','男',78)

对象不仅有函数属性,还有类的数据属性,另外,还有它自己的数据属性。它们都可以通过 . 来访问

对象的 __dict__ 属性只有函数属性,说明了对象只有数据属性

要使用 __init__ 来设置初始化一个对象的方法,它没有返回值。

对于每一个类里定义的参数,都必须要有 self 形参,且必须要有,还要放在第一个位置。

5. 类属性增删改查

类属性的操作使用 .del 进行操作,当使用类进行类属性的更改,它的所有的实例对象,不管是改变之前创建的,或者是改变之后创建的,都会跟着改变;当使用对象对类属性重新赋值之后,类属性实际不会改变,而这个对象的类属性转变为对象属性。数据属性与函数属性都一样,同样的方式。

注意: 要调用类的属性,只能通过类名加点或者对象加点的方式调用,如果直接使用变量名,则调用的是全局的变量。调用不到类的属性。

country = '中国'
class N:
    country = '日本'
    num = ['a', 'b',]
    
    def __init__(self):
        print(country)   # 输出的是中国
    
n1 = N()
print(N.num)
print(n1.num)
n1.num.append(1)   # 没有给类属性赋新值,只是改变了类属性,所以结果是类属性改变了
print(N.num)
print(n1.num)

结果:

中国
['a', 'b']
['a', 'b']
['a', 'b', 1]
['a', 'b', 1]

你可能感兴趣的:(lesson 040 —— 面向对象简介)