面向对象的思维去设计程序
什么是面向对象编程
面向对象编程是一种程序的范式,它把程序看成是对不同对象的相互调用,对现实世界建立的一种模型。在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法。
什么是类
class ClassMate: block 类的名字常常以大写开头 驼峰命名法
程序中类的用法 . 专门用来访问属性,本质操作的就是__dict__ OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school'] OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy' OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1 del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x')
什么是类对象
注意类名后面有个冒号,在block块里面就可以定义属性和方法了。当一个类定义完之后,就产生了一个类对象。类对象支持两种操作:引用和实例化。引用操作是通过类对象去调用类中的属性或者方法,而实例化是产生出一个类对象的实例,称作实例对象。比如定义了一个goods类
class goods: name = 'apple' #定义了一个属性 #定义了一个方法 def printName(self): print(self.name) abc=goods() abc.printName() '''apple '''
goods类定义完成之后就产生了一个全局的类对象,可以通过类对象来访问类中的属性和方法了。
在上面代码中注释的很清楚了,name是一个属性,printName( )是一个方法,与某个对象进行绑定的函数称作为方法。一般在类里面定义的函数与类对象或者实例对象绑定了,所以称作为方法;而在类外定义的函数一般没有同对象进行绑定,就称为函数。
python为类内置的特殊属性
类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
更好的理解类对象
class goods: name = 'apple' #定义了一个属性 def __init__(self,name): self.name=name #类对象产生,其实goods默认继承了object类,类对象的产生与object里面的内置方法有关系 print(goods.__dict__) sdsd=goods('scscs') print(goods.__dict__) ''' {'__module__': '__main__', 'name': 'apple', '__init__': <function goods.__init__ at 0x00000237D68798C8>, '__dict__': <attribute '__dict__' of 'goods' objects>, '__weakref__': <attribute '__weakref__' of 'goods' objects>, '__doc__': None} {'__module__': '__main__', 'name': 'apple', '__init__': <function goods.__init__ at 0x00000237D68798C8>, '__dict__': <attribute '__dict__' of 'goods' objects>, '__weakref__': <attribute '__weakref__' of 'goods' objects>, '__doc__': None} 可以看出来类里面的__init__只是初始化类的实例化结果,类的名称空间不发生变化 如果变量name没有被初始化,对象sdsd也可以使用父类的name变量 ''' print(sdsd.__dict__) '''{'name': 'scscs'} '''
属性
class people: name = 'jack' age = 12 p = people() print(p.name,p.age)
定义了一个people类,里面定义了name和age属性,默认值分别为'jack'和12。在定义了类之后,就可以用来产生实例化对象了,这句p = people( )实例化了一个对象p,然后就可以通过p来读取属性了。这里的name和age都是公有的,可以直接在类外通过对象名访问,如果想定义成私有的,则需在前面加2个下划线 ' __'
class people: __name = 'jack' __age = 12 p = people() print(p.__name,p.__age)
程序运行会报错
Traceback (most recent call last): File "C:/PycharmProjects/FirstProject/oop.py", line 6, in <module> print p.__name,p.__age AttributeError: people instance has no attribute '__name
提示找不到该属性,因为私有属性是不能够在类外通过对象名来进行访问的。在Python中没有像C++中public和private这些关键字来区别公有属性和私有属性,它是以属性命名方式来区分,如果在属性名前面加了2个下划线'__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)
类即类型
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
#类型dict就是类dict >>> list <class 'list'> #实例化的到3个对象l1,l2,l3 >>> l1=list() >>> l2=list() >>> l3=list() #三个对象都有绑定方法append,是相同的功能,但内存地址不同 >>> l1.append <built-in method append of list object at 0x10b482b48> >>> l2.append <built-in method append of list object at 0x10b482b88> >>> l3.append <built-in method append of list object at 0x10b482bc8> #操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3 >>> l1.append(3) >>> l1 [3] >>> l2 [] >>> l3 [] #调用类list.append(l3,111)等同于l3.append(111) >>> list.append(l3,111) #l3.append(111) >>> l3 [111]
补充: 我们都知道Python一切皆对象,那么Python究竟是怎么管理对象的呢?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1、无处不在的__dict__ 首先看一下类的__dict__属性和类对象的__dict__属性 由此可见, 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的 对象的__dict__中存储了一些self.xxx的一些东西 2、Python里什么没有__dict__属性 虽然说一切皆对象,但对象也有不同,就好比不是每个人的女朋友都是一个人一样,一些内置的数据类型是没有__dict__属性的,如下 num = 3 ll = [] dd = {} print num.__dict__ print ll.__dict__ print dd.__dict__ 3、发生继承时候的__dict__属性 子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中 1)每个类的类变量、函数名都放在自己的__dict__中 2)子类对象可以用父类的__dict__里面的值 总结: 1) 内置的数据类型没有__dict__属性 2) 每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__ 3) 对象也有自己的__dict__属性, 存储self.xxx 信息
方法
在类中可以根据需要定义一些方法,定义方法采用def关键字,在类中定义的方法至少会有一个参数,一般以名为'self'的变量作为该参数(用其他名称也可以),而且需要作为第一个参数。下面看个例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class people: __name = 'jack' __age = 12 def getName(self): return self.__name def getAge(self): return self.__age p = people() print p.getName(),p.getAge()
类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
总结:类的数据属性是大家共有的,而且大家的内部地址是一样的,用的就是一个 类的函数属性是绑定到大家身上的,内部地址不一样,绑定方法指的是绑定到对象身上。 绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当做第一个参数传入。 定义在类内部的变量,是所有对象共有的,id全一样, 如果类想调用绑定方法,就必须遵循函数的参数规则,有几个参数,就必须传递几个参数。 定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.fun()会把obj本身当做一个参数来传递。 在类内部定义的函数虽然可以由类来调用,但是并不是为了给类用的,在类内部定义的函数的目的就是为了绑定到对象身上的。 在类的内部来说,__init__是类的函数属性,但是对于对象来说,就是绑定方法。 命名空间的问题:先从对象的命名空间找,随后在从类的命名空间找,随后在从父类的命名 空间找。 在定义类的时候,可以想什么先写什么。
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)
s1.learn() #等同于OldboyStudent.learn(s1)
s2.learn() #等同于OldboyStudent.learn(s2)
s3.learn() #等同于OldboyStudent.learn(s3)
注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
如果对self不好理解的话,可以把它当做C++中类里面的this指针一样理解,就是对象自身的意思,在用某个对象调用该方法时,就将该对象作为第一个参数传递给self。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#类的数据属性是所有对象共享的,id都一样 print(id(OldboyStudent.school)) print(id(s1.school)) print(id(s2.school)) print(id(s3.school)) ''' 4377347328 4377347328 4377347328 4377347328 ''' #类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样 #ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准 print(OldboyStudent.learn) print(s1.learn) print(s2.learn) print(s3.learn) ''' <function OldboyStudent.learn at 0x1021329d8> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x1021466d8>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146710>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146748>>
注意点:
构造方法__init__(self,....):在生成对象时调用,可以用来进行一些初始化操作,不需要显示去调用,系统会默认去执行。构造方法支持重载,如果用户自己没有重新定义构造方法,系统就自动执行默认的构造方法。
__init__():__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望得到的初始化
# Filename: class_init.py class Person: def __init__(self, name): self.name = name def sayHi(self): print 'Hello, my name is', self.name p = Person('Swaroop') p.sayHi() 输出: Hello, my name is Swaroop #__init__的必须注意的点 1、该方法内可以有任意的python代码 2、一定不能有返回值