1.面向对象和面向过程
面向过程就是所有的事情已过程为主,关注的重点是过程,即第一步、第二步、第三步是什么。C语言就是典型的面向过程的语言。C语言需要只要先找到main函数,在main函数中一步一步的往下执行,比如如下代码:
Print('step1')
print('step2')
print('step3')
面向对象的关注点则是对象。比如一个人是一个对象,这个人身高、体重就是对象的属性,而跑、跳则是这个对象可以执行的相关操作。比如如下代码:
class 类名:
def 方法1();
def 方法2():
简而言之,编写函数就是“面向过程”,编写类就是“面向对象”。
2.类和对象
对象:具有行为和属性。在进行描述的时候,属性多为名字。行为多为动词。
类:指一个类别。具有相同属性和行为的“对象”构成的一个整体。
类和对象之间的关系:
类是对象的抽象表现,对象是类的具体表现
类是设计的蓝图,对象是根据蓝图设计出来的具体产物。
3.类的定义
在Python中,通过class关键字来定义一个类。这个类中可以包含零个或多个属性和方法。
class 类名:
def 方法1();
def 方法2():
4.类的实例-对象
当可以实现一个类之后,就可以通过这个类来实例化一个对象。这个对象包含了这个类的全部属性和方法。
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
5.动态增加属性和方法
很多时候我们需要根据需求给对象添加特殊的属性或方法,而这些属性和方法又不能跟其他对象共用,此时就需要动态来添加属性和方法了。
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 <module>
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 <module>
zhangsan.age()
AttributeError: 'person' object has no attribute 'age'
6.init(self)函数
在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
7.传入参数赋值属性
很多时候,每个对象的属性值都是不相同的,就比如我们上边的代码,创建一个“名字”的方法,但是这个方法打印的内容都是相同的。这样就没有版本区分不同的对象了。所以,我们可以通过传入参数的方式赋值不同的属性值。
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
8.类属性
类属性与当前类有绑定关系,与当前类创建的对象无关系。
对于类属性,可以通过类名进行访问,也可以通过对象进行访问。但是,通过对象进行访问,是“只读”,不能进行修改。
对于实例属性,只能通过创建的对象进行访问,不能通过类名进行访问。
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"属性名名字相同罢了。
9.类方法
类方法需要使用@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
10.私有变量(private)
如果希望类中的属性为私有属性,可以通过在属性名称前加两个下划线__来实现。此时只能通过内部进行访问,外部不能访问。
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 <module>
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 <module>
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
11.继承
当有两个属性高度类似的类时,就可以使用继承。如果类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 <module>
print(laowang.gender)
AttributeError: 'fater' object has no attribute 'gender'
12.多重继承
有时一个对象需要继承多个父类的属性,这就是多重继承。
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
13.多态
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,由运行时该对象的确切类型决定,这就是多态的威力。调用方只管调用,不管细节,而当需要新增一种子类型是,只要确保方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则。