面向过程就是所有的事情已过程为主,关注的重点是过程,即第一步、第二步、第三步是什么。C语言就是典型的面向过程的语言。C语言需要只要先找到main函数,在main函数中一步一步的往下执行,比如如下代码:
- Print('step1')
- print('step2')
- print('step3')
面向对象的关注点则是对象。比如一个人是一个对象,这个人身高、体重就是对象的属性,而跑、跳则是这个对象可以执行的相关操作。比如如下代码:
- class 类名:
- def 方法1();
-
- def 方法2():
简而言之,编写函数就是“面向过程”,编写类就是“面向对象”。
对象:具有行为和属性。在进行描述的时候,属性多为名字。行为多为动词。
类:指一个类别。具有相同属性和行为的“对象”构成的一个整体。
类和对象之间的关系:
类是对象的抽象表现,对象是类的具体表现
类是设计的蓝图,对象是根据蓝图设计出来的具体产物。
在Python中,通过class关键字来定义一个类。这个类中可以包含零个或多个属性和方法。
- class 类名:
- def 方法1();
-
- def 方法2():
当可以实现一个类之后,就可以通过这个类来实例化一个对象。这个对象包含了这个类的全部属性和方法。
- class person: #类
- def a_name(self): #方法
- print('xiaoming')
-
- xiaoming = person() #根据person类实例一个对象
- xiaohong = person() #根据person类实例一个对象
- zhangsan = person() #根据person类实例一个对象
-
- xiaoming.a_name() #调用对象的方法
- xiaohong.a_name() #调用对象的方法
- zhangsan.a_name() #调用对象的方法
-
- 结果:
- xiaoming
- xiaoming
- xiaoming
很多时候我们需要根据需求给对象添加特殊的属性或方法,而这些属性和方法又不能跟其他对象共用,此时就需要动态来添加属性和方法了。
- class person: #类
- def a_name(self): #方法
- print('xiaoming')
-
- def age(self): #动态增加的方法
- print("18")
-
- xiaoming = person() #根据person类实例一个对象
- xiaohong = person() #根据person类实例一个对象
- zhangsan = person() #根据person类实例一个对象
-
- xiaoming.gender = 'boy' #动态增加属性gender
- xiaoming.age = age #动态增加方法age
-
- xiaoming.a_name() #调用对象的方法
- xiaohong.a_name() #调用对象的方法
- zhangsan.a_name() #调用对象的方法
-
- print(xiaoming.gender)
- xiaoming.age(xiaoming)
-
- print(zhangsan.gender)
- zhangsan.age(zhangsan)
-
- 结果:
- xiaoming
- xiaoming
- xiaoming
- boy
- 18
- Traceback (most recent call last):
- File "d:/python/test_project/test.py", line 37, in
- print(zhangsan.gender)
- AttributeError: 'person' object has no attribute 'gender'
-
- Traceback (most recent call last):
- File "d:/python/test_project/test.py", line 38, in
- zhangsan.age()
- AttributeError: 'person' object has no attribute 'age'
在python的init函数中,init前后各有2个下划线。而Init在每次被调用创建对象时,都会“自动调用”一次。参数self是必不可少的。类中定义的方法,第一个参数是固定的,命名为"self"。
- class person: #类
- def __init__(self):
- print("init")
- def a_name(self): #方法
- print('xiaoming')
-
- xiaoming = person() #根据person类实例一个对象
- xiaohong = person() #根据person类实例一个对象
- zhangsan = person() #根据person类实例一个对象
-
- 结果:
- init
- init
- init
很多时候,每个对象的属性值都是不相同的,就比如我们上边的代码,创建一个“名字”的方法,但是这个方法打印的内容都是相同的。这样就没有版本区分不同的对象了。所以,我们可以通过传入参数的方式赋值不同的属性值。
- class person: #类
- def __init__(self,name,age):
- self.name = name #属性
- self.age = age #属性
- def action(self): #方法
- print('%s is running,age %d' %(self.name,self.age))
-
- xiaoming = person('xiaoming',18) #根据person类实例一个对象
- zhangsan = person('zhangsan',20) #根据person类实例一个对象
-
- xiaoming.action() #对象执行方法
- zhangsan.action() #对象执行方法
-
- 结果:
- xiaoming is running,age 18
- zhangsan is running,age 20
-
类属性与当前类有绑定关系,与当前类创建的对象无关系。
对于类属性,可以通过类名进行访问,也可以通过对象进行访问。但是,通过对象进行访问,是“只读”,不能进行修改。
对于实例属性,只能通过创建的对象进行访问,不能通过类名进行访问。
- class person: #类
- city = 'beijing' #类属性
- def __init__(self,name,age):
- self.name = name
- self.age = age
- def action(self): #方法
- print('%s is running,age %d' %(self.name,self.age))
-
- xiaoming = person('xiaoming',18) #根据person类实例一个对象
- zhangsan = person('zhangsan',20) #根据person类实例一个对象
-
- print(person.city) #打印类属性
- xiaoming.city = 'shanghai' #通过对象修改类属性
- print(xiaoming.city) #打印对象类属性
- print(person.city) #打印类属性
- print(zhangsan.city) #打印对象类属性
-
- print('>>>>>>>>>>>>>>')
- person.city = 'nanjing'
- print(xiaoming.city) #打印对象类属性
- print(person.city) #打印类属性
- print(zhangsan.city) #打印对象类属性
-
- 结果:
- beijing
- shanghai
- beijing
- beijing
- >>>>>>>>>>>>>>
- shanghai
- nanjing
- nanjing
从上述代码中,通过对象修改了类属性,看似修改成功了,其实并不是修改了类属性,只是动态地给对象创建了一个属性“city”,只不过这个属性名跟类属性的"city"属性名名字相同罢了。
类方法需要使用@classmethod修饰
类方法的第一个参数是固定的,命名为cls
有两种方式来访问类方法,一种是通过类名来访问,另一种是通过对象名来访问(不建议)。
最好是通过类方法来访问类属性,通过实例方法来访问实例属性。
- class person: #类
- city = 'beijing' #类属性
- def __init__(self,name,age):
- self.name = name
- self.age = age
- def action(self): #方法
- print('%s is running,age %d' %(self.name,self.age))
-
- @classmethod
- def move_city(cls,city): #类方法
- cls.city = city
-
- xiaoming = person('xiaoming',18) #根据person类实例一个对象
- zhangsan = person('zhangsan',20) #根据person类实例一个对象
-
- print(person.city)
- person.move_city('shenzhen') #通过类名访问
- print(person.city)
- xiaoming.move_city("guangzhou") #通过对象名访问
- print(person.city)
-
- 结果:
- beijing
- shenzhen
- guangzhou
如果希望类中的属性为私有属性,可以通过在属性名称前加两个下划线__来实现。此时只能通过内部进行访问,外部不能访问。
- class person: #类
- city = 'beijing' #类属性
- def __init__(self,name,age):
- self.__name = name #私有属性
- self.__age = age #私有属性
- def action(self): #方法
- print('%s is running,age %d' %(self.name,self.age))
-
- def get_name(self): #访问私有属性
- print(self.__name)
-
- def get_age(self): #访问私有属性
- print(self.__age)
-
- xiaoming = person('xiaoming',18) #根据person类实例一个对象
- zhangsan = person('zhangsan',20) #根据person类实例一个对象
-
- xiaoming.get_name()
- xiaoming.get_age()
-
- 结果:
- xiaoming
- 18
如果直接调用实例进行访问的话,则会报错
- print(xiaoming.__name)
-
- 结果:
- Traceback (most recent call last):
- File "d:/python/test_project/test.py", line 38, in
- print(xiaoming.__name)
- AttributeError: 'person' object has no attribute '__name'
那为什么要设置私有变量呢?因为这样可以在方法中对传入的参数进行判断,避免无效的参数输入。
- class person: #类
- def __init__(self,name,age):
- self.__name = name #私有属性
- self.__age = age #私有属性
-
- def set_age(self,s_age):
- if not isinstance(s_age,(int)):
- raise TypeError('age false type')
-
- if(s_age > 0 and s_age < 100):
- self.__age = s_age
- print('age %d' %self.__age)
- else:
- print('age not correct:%d' %s_age)
-
- xiaoming.set_age(18)
- xiaoming.set_age(200)
- xiaoming.set_age('18')
-
- 结果:
- age 18
- age not correct:200
- Traceback (most recent call last):
- File "d:/python/test_project/test.py", line 52, in
- xiaoming.set_age('18')
- File "d:/python/test_project/test.py", line 29, in set_age
- raise TypeError('age false type')
- TypeError: age false type
当有两个属性高度类似的类时,就可以使用继承。如果类A继承了类B,那么A就是子类,B就是父类。子类一旦继承了父类,那么子类就会具备父类的一切特征。因此,父类能做的事情,子类也都可以做。
- class fater: #父类
- def __init__(self):
- self.name = 'fater'
- self.age = 58
-
- class child(fater): #子类
- def __init__(self):
- super().__init__() #调用父类的init方法
- self.gender = 'boy'
-
- laowang = fater(); #实例父类
- print(laowang.name) #打印父类属性
- xiaowang = child(); #实例子类
- print(xiaowang.name) #打印子类继承父类的属性
- print(xiaowang.gender) #打印子类特有的属性
- print(laowang.gender) #打印父类中子类的属性(报错)
-
- 结果:
- fater
- fater
- boy
- Traceback (most recent call last):
- File "d:/python/test_project/test.py", line 64, in
- print(laowang.gender)
- AttributeError: 'fater' object has no attribute 'gender'
有时一个对象需要继承多个父类的属性,这就是多重继承。
- class fater: #父类
- def __init__(self,name,age):#属性
- self.name = name
- self.age = age
-
- def get_name(self):#方法
- print('fater name:%s' %self.name)
-
- class mother: #父类
- def __init__(self,name,age) -> None: #属性
- self.name = name
- self.age = age
-
- def get_name(self):#方法
- print('mother name:%s' %self.name)
-
- class child(fater,mother):#子类,按照先后顺序进行继承
- def __init__(self, name, age):#属性
- super().__init__(name, age) #调用父类的init方法
- self.gender = 'boy'
-
- ft = fater('laowang',58); #实例
- mt = mother('laohong',55) #实例
- kid = child('xiaoming',10) #实例
- print(ft.name)
- print(mt.name)
- print(kid.name) #父类属性
- print(kid.age) #父类属性
- print(kid.gender) #子类属性
- kid.get_name() #父类方法
-
- 结果:
- laowang
- laohong
- xiaoming
- 10
- boy
- fater name:xiaoming
可以看到,虽然同时继承了father和mother,但是调用父类的"name"方法时,调用的还是fater的方法。因为在继承时,father在前。
那把mother改到前面后试试。
- class child(mother,fater):#子类,按照先后顺序进行继承
-
- 结果:
- mother name:xiaoming
- class fater: #父类
- def __init__(self,name,age):#属性
- self.name = name
- self.age = age
-
- def get_name(self):#方法
- print('fater name:%s' %self.name)
-
-
- class child(fater):#子类
- def __init__(self, name, age):#属性
- super().__init__(name, age) #调用父类的init方法
- self.gender = 'boy'
-
- def get_name(self):
- print('child name:%s' %self.name)
-
- ft = fater('laowang',58); #实例
- kid = child('xiaoming',10) #实例
-
- kid.get_name()
-
- 结果:
- child name:xiaoming
可以看到,child是从father处继承了属性和方法,child和father有同名的方法get_name,但是当调用child的get_name方法时,会自动调用自身的方法,而不会调用父类的方法。这就是多态。
对于一个变量,只需要知道父类型,无需确切地知道它的子类型,就可以放心地调用方法,而具体调用的方法是作用在father还是child,由运行时该对象的确切类型决定,这就是多态的威力。调用方只管调用,不管细节,而当需要新增一种子类型是,只要确保方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则。