一,编程范式
所谓编程范式(programming paradigm),指的是计算机编程的基本风格或典范模式。怎么说呢,每个人都有自己不同的习惯,当然编程也是一样的,每个程序组员根据自己不同的习惯会写出不同的代码。当然这样肯定是不行的啦,这样的可读性不强,而且太乱了,所以慢慢慢慢的大家就统一编程的风格。编程范式里面呢又包括下面几种(我知道的):面向对象,面向过程,函数式编程。感觉写到这里,需要用一波我自己的土话来跟大家解释一下这三种的大概不同了。
面向对象:在python里面一切皆对象,当然不是你女(男)朋友那个对象。emmm,打个比方吧,假如你高考完了,准备选学校,选专业了,你在想到底是去北大呢,还是去清华呢。你就想可以写个函数,如果选清华有哪些专业,选北大有哪些专业。当然还可以这样写,你可以把清华看成一个对象,北大看成一个对象,然后里面的对象有不同的属性跟方法。你选择了清华,就是实例化了一个清华对象,下一个人选择北大,就实例化一个北大对象。这样看似没得多大区别,但是他能够在里面的属性跟方法添加其他的,可扩展性更强了,而且容易修改,这种思想其实就是面向对象。
面向过程:面向过程就太好理解了。因为我们这种小白,最熟悉的就是面向过程。面向过程呢,就是一步一步实现,然后到最后完成。面向过程就是重要的是过程,但是在使用面向过程时,你想改其中一个地方,你会发现很多地方都要改,一个地方错了,其他地方也错了,所以很麻烦。
函数式编程:其实,我觉得跟面向过程差不多,然后是用函数实现的一种编程方式。
二,面向对象简介
1,认识面向对象
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
2,面向对象特性
面向对象有三大特性:封装,继承,多态
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。在这里值得提一下的就是,python支持多继承,之后多继承后面也会提到。通俗来讲,继承就是你继承其他的类,他的属性方法,你都有,而且还可以继续增加或者修改一些属性或者方法。
多态:多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
三,面向对象基础语法
听了这么多的类啊,对象啊,现在就来新建一个类吧。在新建之前,类还分经典类和新式类。
经典类:
1 import time 2 class people:#(class后面的是类名) 3 #构造函数,在实例化一个对象的时候生成的一些属性写在这里 4 def __init__(self,name): 5 self.name=name 6 def eat(self): 7 print('%s is eating...'%self.name) 8 def sleep(self,times): 9 print('%s Is sleeping'%self.name) 10 time.sleep(times) 11 print('%s sleep up' %self.name) 12 a=people('zzq') #实例化一个对象 13 a.eat() #调用里面的方法 14 a.sleep(1)#调用需要传参的方法
新式类:(继承了object)
1 import time 2 class people(object):#(class后面的是类名) 3 #构造函数,在实例化一个对象的时候生成的一些属性写在这里 4 def __init__(self,name): 5 self.name=name 6 def eat(self): 7 print('%s is eating...'%self.name) 8 def sleep(self,times): 9 print('%s Is sleeping'%self.name) 10 time.sleep(times) 11 print('%s sleep up' %self.name) 12 a=people('zzq') #实例化一个对象 13 a.eat() #调用里面的方法 14 a.sleep(1)#调用需要传参的方法
他们之间的区别在讲了后面的继承跟多继承会讲到,其实在python3中没得区别,一般常用新式类的写法哦。
四,类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
在python中继承中的一些特点:
- 1、如果在子类中需要父类的构造方法就需要显示的调用父类的构造方法,或者不重写父类的构造方法。详细说明可查看:python 子类继承父类构造函数说明。
- 2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
- 3、Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
1,下面简单介绍一下,继承的一些基本语句
1 class Father(object): 2 def __init__(self,name): 3 self.name=name 4 def get_name(self): 5 print('Father’s name is %s' %self.name) 6 def say(self): 7 print('Father is say') 8 class Son(Father):#继承Father类 9 def __init__(self,name,age): 10 Father.__init__(self,name)#继承父类的构造方法 11 self.age=age 12 def get_name(self): 13 Father.get_name(self)#继承父类的方法 14 print('Son’s name is %s age is %s'%(self.name,self.age)) 15 def say(self): 16 print('Son is say')#重写say方法 17 a=Son('xxx','18') 18 a.get_name() 19 a.say()
执行结果:
在上面的构造方法的继承其实还有一种比较常用的,也是推荐使用的方法,代码如下
1 def __init__(self,name,age): 2 # Father.__init__(self,name)#继承父类的构造方法 3 super(Son, self).__init__(name)#继承父类的构造方法,并且不用知道父类的名字 4 self.age=age
总结一下吧。如果不重构的情况下,默认,子类有父类所有的方法并且跟父类相同。子类还可以继承父类的构造方法或者其他方法,并且在上面添加扩展。如果父类跟子类的方法或者属性重名,那么使用的还是子类的属性或方法。
2,多继承的用法跟分析
在讲到多继承这里大家就要知道在继承的时候需要考虑继承的顺序问题。讲这么多不好理解,我们先看代码,然后在分析吧。
1 class F1(object): 2 def say1(self): 3 print('this is F1 say1') 4 def say2(self): 5 print('this is F1 say2') 6 class F2(object): 7 def say1(self): 8 print('this is F2 say1') 9 def say2(self): 10 print('this is F2 say2') 11 def say3(self): 12 print('this is F2 say3') 13 class Son(F1,F2):#继承F1,F2两个类 14 pass 15 a=Son() 16 a.say1() 17 a.say2() 18 a.say3()
执行结果:
1 this is F1 say1 2 this is F1 say2 3 this is F2 say3
通过结果我们可以看到,如果同时继承了两个类,会首先去前面一个类里面找有木有方法或者属性,如果有,就用前面那个类的属性或方法,如果前面没有就用后面的那个类的属性或方法。
讲到这里跟大家讲一下经典类跟新式类的区别吧。但是在讲区别之前,需要带大家了解一下什么叫广度优先跟深度优先。
广度优先:从初始点出发,把所有可能的路径都走一遍,如果里面没有目标位置,则尝试把所有两步能够到的位置都走一遍,看有没有目标位置;如果还不行,则尝试所有三步可以到的位置。这种方法,一定可以找到一条最短路径,但需要记忆的内容实在很多,要量力而行。
深度优先:从初始点出发,不断向前走,如果碰到死路了,就往回走一步,尝试另一条路,直到发现了目标位置。这种不撞南墙不回头的方法,即使成功也不一定找到一条好路,但好处是需要记住的位置比较少。
python2经典类是按照深度优先来继承类的,新式类是按照广度优先来继承类的。
python3经典类和新式类都是统一按照广度优先来继承类的。
当然,有的人还是不能完全理解继承流程,下面是我画的流程图,当然,丑不能怪我!!!
这个顺序是在继承的时候查找属性和方法的向上查找顺序。
五,类属性,方法扩展
想了一下,属性好像有实例变量,类变量,私有变量等。方法有构造方法,析构函数,私有方法,静态方法,类方法,属性方法等。
1,私有变量,私有方法
私有属性跟私有方法,其实就是只能在类里面能够使用的属性跟方法,外部不能调用使用。
1 class tset(object): 2 def __init__(self,name): 3 self.__name=name #变为私有属性,只有内部能够调用 4 def __say(self): #变为私有方法,只有内部能够调用 5 print(self.__name) #调用私有属性 6 def say(self): 7 self.__say() #调用私有方法
2,静态方法
普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。
1 class Test(object): 2 def __init__(self, name): 3 self.name = name 4 @staticmethod # 把say方法变为静态方法 5 def say():#不能传入其他参数 6 print("hello") 7 d = Test("xxx") 8 d.say()
3,类方法
类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量
1 class Test(object): 2 name='zzq' #类变量 3 def __init__(self, age): 4 self.age = age 5 @classmethod # 把say方法变为类方法 6 def say(self): 7 print("hello %s" %self.name)#只能调用类变量 8 d = Test("xxx") 9 d.say()
4,属性方法
属性方法的作用就是通过@property把一个方法变成一个静态属性
1 class Test(object): 2 def __init__(self, name): 3 self.name = name 4 @property # 把say方法变为属性方法 5 def say(self): 6 print("hello %s" %self.name) 7 d = Test("xxx") 8 d.say #相当于say是一个属性,调用的时候相当于调用一个属性,不用加括号
六,类里面的一些特殊方法
1,__str__
1 def __str__(self):#输出对象时,输出的是return的返回值 2 return 'hahaha'
2,__call__
1 def __call__(self, *args, **kwargs):#在调用后面加一个()执行这个方法 2 print('this is call',args,kwargs) 3 Test("xxx")() #在调用的时候后面再加一个括号
3,__doc__,__module__
1 print(Test.__doc__)#打印类的描述 2 print(Test.__module__)#输出类所在哪个模块
4,__class__
print(Test.__class__)#表示当前操作的对象的类是什么
5,__dict__
1 print(Test.__dict__)#打印类中的所有属性,不包括实例化属性 2 a=Test("xxxx") 3 print(a.__dict__)#打印类中所有实例化属性,不包括类属性
6,__del__
1 def __del__(self):#析构函数,在实例化以后自动调用,比如一些关闭数据库连接等操作 2 print('end')
7,__new__
类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__,所以在实例化__init__之前其实先调用了__new__方法的。
七,反射
相信在写程序的时候都会遇到一个问题就是你想要把字符串转换成需要执行的属性或者类。需要执行对象里的某个方法,或需要调用对象中的某个变量,但是由于种种原因我们无法确定这个方法或变量是否存在,这是我们需要用一个特殊的方法或机制要访问和操作这个未知的方法或变量,这中机制就称之为反射。
简单来讲,反射就是把字符串转化为可以执行的属性或者方法。
1 class Dog(object): 2 def __init__(self,name): 3 self.name=name 4 def eat(self): 5 print('The dog %s is eating...' %self.name) 6 d=Dog("xxx") 7 chose=input(">>>>") 8 print(hasattr(d,chose)) #判断输入的chose在d对象中,是否存在属性或者方法 9 10 func=getattr(d,chose) #把字符串转化为可以直接使用的属性或者方法 11 func() #如果是方法,调用方法(执行结果:The dog xxx is eating...) 12 print(func) #如果是属性(执行结果:xxx) 13 14 setattr(d,'age',20)#为d增加属性,第二个为属性名,第三个为属性值 15 print(d.age) #执行结果:20 16 def say(self): 17 print('hello,world') 18 setattr(d,'talk',say)#把函数say添加到d中,并且函数名为talk 19 d.talk(d) #调用talk方法,因为这是额外添加的方法,需手动传入对象(执行结果:hello,world) 20 21 delattr(d,'name')#删除d中的name属性,delattr不能删除类中的方法
当然,一般hasattr跟getattr是一起使用的,先用hasattr判断类或对象中是否存在,然后再用getattr获取。getattr不仅仅可以获取对象的方法和属性和方法,还可以获取类的方法和属性。你还可以这样理解,比如d.name在使用getattr的时候可以理解为getattr(obj,str)obj相当于.前面的,str相当于.后面的。