(二十)类与对象 ---- 基础知识

面向对象的设计

是一种编程模型设计,即把特征(数据属性)与技能(方法属性)整合作为一个结合体,这个结合体即对象,一切皆为对象。

基于面向对象设计程序就好比在创造一个世界,你就是这个世界的上帝,世间存在的万物皆为对象,不存在的也可以创造出来。

好比西游记中,如来佛祖要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取经(面向过程)。

注意,这是一种程序设计思想,很多人存在一个误区:只有用class定义类的代码才是面向对象

def dog (name,type):
    def jiao(dog):
        print('一条叫%s的狗,汪汪汪'%dog['name'])
    def init(name,type):
        dog = {'name':name , 'type':type , 'jiao':jiao}  #name和type是数据属性,jiao是方法属性,三者用一个字典封装,用来初始化实例
        return dog
    return init(name,type)

d1 = dog('小强','二哈')                                #实例化一个对象d1
print(d1)
d1['jiao'](d1)
结果:

{'name': '小强', 'type': '二哈', 'jiao': <function dog.<locals>.jiao at 0x000001DB61F946A8>}
一条叫小强的狗,汪汪汪

这个例子用函数嵌套的的方式,没有用到class,却是一个地地道道的面向对象程序设计,狗的姓名和种类是数据属性,叫是狗的方法属性,这三者作为一个结合体,组成一个'dog类',来实例化一个个对象。

面向对象的编程

用定义类+对象(实例)的方式去实现面向对象的设计

面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD完全可以由纯结构化语言来实现,只不过要想构造具备对象性质和特点的数据类型,就需要在程序上做更多的努力。比如用纯C写的linux kernel其实比c++/java写的大多数项目更加面向对象,linus的泛文件抽象(一切皆文件)恰恰就是面向对象的设计。

所以面向对象编程和面向对象设计是八竿子打不着两码事,一个是方法论,一个是设计思路

类与对象

类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体,调用类就可以实例化一个个对象
那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
在现实世界中:先有对象,再有类
世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念
也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在
在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果return,而调用类会产生对象,返回的是对象

 实例化:

class student:
    job = '上学'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print('%s背着书包上学' %self.name)

p1 = student('小明',18)

print(student.__dict__)        #类的属性列表
print(p1.__dict__)             #对象的属性列表

print(student.job)             #类的数据属性
print(p1.job)                  #对象访问类的数据属性
print(p1.study)                #对象访问类的方法属性

print(p1.name)                 #对象的数据属性
# print(student.name)          #类访问对象的数据属性,报错

p1.study()
结果:

{'__weakref__': <attribute '__weakref__' of 'student' objects>, '__doc__': None, '__dict__': <attribute '__dict__' of 'student' objects>, 'study': <function student.study at 0x000001A60C384620>, '__init__': <function student.__init__ at 0x000001A60C3846A8>, '__module__': '__main__', 'job': '上学'}
{'name': '小明', 'age': 18}
上学
上学
<bound method student.study of <__main__.student object at 0x000001A60C381DA0>>
小明
小明背着书包上学

 

个人总结:

类的实例化即产生一个个对象,通过执行类的__init__()方法

类和对象都有__dict__内置属性,返回一个属性列表;对数据属性和方法属性的所有操作,都可以当做在操作这个__dict__返回的字典

类有数据属性和方法属性,而严格的讲:对象只有数据属性没有方法属性;如下图类的__init__()方法中,只有对数据属性的定义赋值

那为什么每个对象都有方法属性,并可以访问类属性?是因为通过寻址引用的方式;所以,一旦类中的方法属性发生变化,对象中的方法属性也马上变化(不论这个对象是否已经被实例化)

实例化只赋予对象的数据属性,而对象通过寻址引用的方式调用类的方法属性,这样可以节省内存

对象可以访问类的属性,而类无法访问对象的属性,对象的作用域是先找自己的属性列表(__dict__方法),再找类的属性列表

 类属性的增删改查

class student:                                 #定义student类
    job = '上学'                                #类的数据属性
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):                           #类的方法属性
        print('%s背着书包上学' %self.name)
p1 = student('小明',18)                         #实例化一个对象p1

#
print(student.job)                             #类名加.的方式访问类属性
print(p1.job)                                  #对象也可以访问类属性,对象名加.的方式访问类属性
 
#
student.country = '中国'                        #新增类的数据属性
print(p1.country)

def eat_food(self,food):                       #新增类的方法属性
    print('%s正在吃%s' %(self.name,food))
student.eat = eat_food                         #把eat_food函数的内存地址给student类的eat方法属性,这里eat_food不能加括号,加了括号就是eat_food()执行后的返回值
p1.eat('米饭')

#
student.country = '日本'                        #修改类的数据属性
print(p1.country)

def test(self):                                #修改类的方法属性,但方法属性名还是study
    print('test')
student.study = test
p1.study()

#
del student.job
print(p1.job)                                  #删除类的数据属性后会报错

 对象属性的增删改查

class student:
    job = '上学'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print('%s背着书包上学' %self.name)
p1 = student('小明',18)

#
print(p1.age)                                  #查看对象的数据属性
print(p1.study)                                #查看对象的方法属性,是寻址引用类的方法属性

#
p1.female = 'boy'                              #新增对象的数据属性
print(p1.female)

def test():                                    #新增对象的方法属性,一般不这么用,对象不会独自去定义一个类中没有的方法,没有意义
    print('aaa')
p1.test = test
p1.test()

#改                                             #修改对象的数据属性
p1.age = 20
print(p1.age)

#
# del p1.age                                    #删除对象的数据属性
# print(p1.age)
结果:

18
<bound method student.study of <__main__.student object at 0x00000248CB26A828>>
boy
aaa
20

几个注意点

1.通过对象可以访问类的数据属性,但无法修改类的数据属性

class student:
    job = '上学'
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def study(self):
        print('%s背着书包上学' %self.name)
p1 = student('小明',18)

p1.job = '上班'         #这样定义其实是新增了一个p1对象的数据属性,与类的数据属性无关。可以这样理解:一个个对象是类实例化出来的,如果通过一个对象就可以改变类的数据属性,那么其他的对象岂不是也变了
print(p1.job)           #上班
print(student.job)      #上学

但是,也有个例:

class student:
    job = '上学'
    l = ['a','b']
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def study(self):
        print('%s背着书包上学' %self.name)
p1 = student('小明',18)

# p1.l = [1,2,3]                         #通过这种方式不会改变类的数据属性,而是新增了p1对象的数据属性,会在对象的__dict__列表属性中添加,不会在类的__dict__列表属性中添加             
# print(student.l)

p1.l.append('c')                         #通过这种方式会改变类的数据属性,引用了类的数据属性,恰恰是一个list可变数据类型
print(student.l)
结果: ['a', 'b', 'c']

2.加点的调用方式才是类和对象的属性,不加点的调用方式与类和对象都无关

school = '初中'                            #全局变量school
class student:
    job = '上学'
    school = '高中'                        #类的数据属性school
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print(school)                      #初中        不加点的调用方式,这个school既不会去类的属性列表中找,也不会去对象的属性列表中找,所以就是全局变量school
        print(student.school)              #高中        在类的属性列表中找到school这个键值对
    def study(self):
        print('%s背着书包上学' %self.name)
p1 = student('小明',18)
原文地址:https://www.cnblogs.com/xulan0922/p/10321649.html