python面向对象
Posted mengxiaoleng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python面向对象相关的知识,希望对你有一定的参考价值。
面向对象编程
类的定义形式多样
- 既可以直接创建新的类,也可以基于一个或多个已有的类创建新的类;
- 既可以创建一个空的类,然后再动态添加属性和方法,也可以在创建类的同时设置属性和方法。
- 类是对现实世界中一些事物的封装,可以把数据(属性)和操作(方法)封装在一起,从而使得
程序结构更加清晰。 - 使用class保留字定义一个类:
定义类
定义一个类
class Person:
#定义一个属性
name="小明"
#定义一个方法
def getName(self):
return self.name
定义一个空类
class Person:
pass
创建对象
p=Person()
- 类定义好后进行实例化 p=Person( ),产生一个Person类的实例对象
- 实例对象是根据类模版生成一个内存体,系统都会在内存中选择一块区域分配给对象,有确定的
数据与内存地址,每次选择的内存通常是不一样的
构造函数
构造函数:初始化方法,命名为__init__,在对象创建后自动调用
class Person:
name="小明"
def __init__(self):
print(self,self.name)
f=Person()
默认参数
class Person:
def __init__(self,n="小红"):
self.name=n
print(self,self.name)
>>>f=Person()
>>>p=Person("小明")
自定义参数
class Complex:
def __init__(self,realpart,imagpart):
self.real=realpart
self.imag=imagpart
类属性和实例属性
class Person:
name="Max"# 类属性
age=12#类属性
def __init__(self,height):
self.height=height#实例属性
- 类属性可通过两种方式访问:
- 使用类的名称,例如,Person.name, Person.age
- 使用类的实例对象,例如p=Person( )是对象,通过p.name, p.age访问
- 注意:类属性虽然归类所有,但实例化的对象都可以访问类属性
- 实例属性通过实例对象可绑定属性,新建、改变属性
改变类属性
class Person:
name='Max'
age=10
#类型读取类属性
print(Person.name,Person.age)
#类名称改变类属性
Person.name="小明"
Person.age=22
print(Person.name,Person.age)
改变实例属性
通过对象实例改变类属性
格式:对象实例.属性=…
- 如果该对象实例存在这个属性,这个属性的值就被改变。
- 如果该对象实例不存在这个属性,自动为该对象实例创建一个新的属性。
class Person:
name='Max'
age=10
#读取类属性
print(Person.name,Person.age)
p=Person()
p.name='John'
print(p.name,p.age)
print(Person.name,Person.age)
# 'Max' 10
# 'John' 10
# 'Max' 10
相同名称的实例属性优先级高于类属性(覆盖)实例属性改变,不影响类属性
删除实例属性
删除实例属性后,访问该名称对应的属性时,将访问类属性
class Person:
name='Max'
age=12
p=Person()
print(p.name)#Max
p.name='John'
print(p.name)#John
print(Person.name)#Max
del p.name
print(p.name)#Max
不同实例对象属性不同
不同的实例对象,有不同的实例属性
class Person:
name='Max'
age=12
#用类名读取类属性
print(Person.name,Person.age)
# 用对象实例改变类属性
p=Person()
p.name='John'
print(p.name,p.age)
print(Person.name,Person.age)
q=Person()
q.age=21
print(q.name,q.age)
# Max 12
# 'John' 12
# Max 12
# Max 21
建立新的属性
class Person:
name='Max'
age=22
#对象实例改变类属性
p=Person()
p.gender='male'
print(p.name,p.age,p.gender)
Python作为一种动态语言,除了可以在定义类时指定类属性外,还可以动态地为已经创建的对象绑定新的属性
可变对象作为类属性
class Person:
part_time=[]
def __init__(self,name):
self.name=name
def add_part(self,sh):
self.part_time.append(sh)
p=Person('Max')
q=Person('John')
q.add_part('read')
p.add_part('walk')
print(p.part_time)
print(q.part_time)
#['read','walk']
#['read','walk']
# part_time被共享了
使用实例属性替代类属性,以避免实例操作改变类属性变量的值
class Person:
def __init__(self,name):
self.name=name
self.part_time=[]
def add_part(self,sh):
self.part_time.append(sh)
p=Person('Max')
q=Person('John')
q.add_part('read')
p.add_part('walk')
print(p.part_time)
print(q.part_time)
#['walk']
#['read']
#各是各的
小结
- 类属性是与类绑定的,它被这个类所拥有的,如果要修改类的属性,就必须使用类名称访问它,而不能使用对象实例访问它。
- 对象实例的属性默认和类相同,通过赋值语句进行修改、创建后,不同的实例对象,有不同的属性。
- 可变对象作为类属性时,需注意每个实例操作都
可能改变其值。
类方法
实例方法
通过实例对象调用的方法一般以‘self’的变量作为第一个参数(其他名称也可以),self表示对象自身的含义,某个实例对象调用该方法时,将该对象作为第一个参数传递给self
class Person:
name='Max'
age=12
def getName(self):
return self.name
def getAge(self):
return self.age
p=Person()
print(p.getName(),p.getAge())
#Max 12
p.getName( )时,将p传递给self,执行return p.name得 到name,类似地,通过p.getAge( )得到age
实例方法调用
- 一般使用实例对象调用,调用时向实例方法传递实例参数,例如p.getName( )
- 也可以用类名称Person调用,但要传递实例参数,例如Person.getName(p)
class Person:
name='Max'
age=12
def getName(self):
return self.name
def getAge(self):
return self.age
p=Person()
print(p.getName(),p.getAge())
print(Person.getName(p),Person.getAge(p))
#Max 12
#Max 12
类方法
定义
在类中可以定义类的方法
- 通过@classmethod 修饰
- 第一个参数一般命名为cls (也可以是其他名称)
class Person:
name='Max'
age=12
@classmethod
def show(cls):
print(cls.name,cls.age)
调用
一般通过类名称调用,调用时传递类参数,例如Person.show( ), 将Person 传递给cls,因此
print(cls.name,cls.age)等价于执行print(Person.name,Person.age)
class Person:
name='Max'
age=12
@classmethod
def show(cls):
print(cls.name,cls.age)
Person.show()
也可以通过实例对象调用,这时候对象p的类Person会传递给函数的参数cls
class Person:
name='Max'
age=12
@classmethod
def show(cls):
print(cls.name,cls.age)
p=Person()
p.show()
静态方法
通过@staticmethod修饰,无参数传递
class Person:
name='Max'
age=12
@staticmethod
def display():
print(Person.name,Person.age)
通过类名称调用,不传递参数,与类方法调用不同(传递类参数)
静态方法调用
通过类名称调用,不传递参数,与类方法调用不同(传递类参数)
class Person:
name='Max'
age=12
@classmethod
def show(cls):
print(cls.name,cls.age)
@staticmethod
def display():
print(Person.name,Person.age)
>>>Person.show()
>>>'Max' 12
>>>Person.display()
>>>'Max' 12
通过实例对象调用,不传递参数
class Person:
name='Max'
age=12
@staticmethod
def display():
print(Person.name,Person.age)
p=Person()
p.display()
#Max 12
小结
- 实例方法
- self
- 对象实例调用p.getName( ),类名称调用
- (Person.getName(p)、传递对象实例参数
- 类方法
- @classmethod, cls
- 类名称调用Person.show( )、对象实例调用 p.show( ),传递类参数
- 静态方法
- @staticmethod
- 类名称调用Person.display ( )、对象实例调用p.display( ),不传递参数
访问权限
Person类中的name 和age属性是公有的,可以直接在类外访问
class Person:
name='Max'
age=12
私有变量
如果想定义为私有的,在属性前面加两个下划线‘__’
class Person:
__name='Max'
age=12
私有属性不能直接在类外访问
Person.name
#报错
私有属性,可以被类内定义的方法访问
class Person:
__name='Max'
age=12
def getName(self):
return self.__name
p=Person()
p.show()
私有方法
可以在类内定义私有方法,在方法名称前加两个下划线‘__’
class Person:
__name='Max'
age=12
def __getName(self):
return self.__name
私有方法不能直接在类外调用
class Person:
__name='Max'
age=12
def __getName(self):
return self.__name
p=Person()
name=p.getName()
私有方法可被类内定义的其它方法调用
class Person:
__name='Max'
age=12
def __show(self):
print(self.name)
def display(self):
self.__show()
p=Person()
p.display()
小结
- 在共有属性/方法前加两个下划线‘__’,即变成私有属性/方法
- 公有属性/方法可以在类外直接访问/调用
- 私有属性/方法不可以再类外直接访问/调用
- 在类定义的内部,可以访问私有属性/方法
继承
继承的语法形式
- DerivedClassName为子类,BaseClassName为父类
(又称基类、超类)
class DerivedClassName(BaseClassName):
pass
子类通过继承可以获得父类的所有方法
class Person:
def __init__(self,name):
self.name=name
def getName(self):
return self.name
class Student(Person):
pass
p=Student('Max')
print(p.name)
print(p.getName())
重写
对于父类的方法,子类实现重新定义
class Person:
def __init__(self,name):
self.name=name
def read(self):
print('The Person '+self.name+' is reading...')
class Student(Person):
def read(self):
print('The Student'+self.name+' is reading...')
p=Person('xiaoming')
p.read()
>>>The Person xiaoming is reading...
q=Student('xiaohong')
q.read()
>>>The Student xiaohong is reading...
若不想子类覆盖基类的方法,可将方法设为私有
class Person:
def __init__(self,name):
self.name=name
def __read(self):
print('The Person '+self.name+' is reading...')
def test(self):
self.__read()
class Student(Person):
def __read(self):
print('The Student'+self.name+' is reading...')
p=Person('xiaoming')
p.test()
>>>The Person xiaoming is reading...
q=Student('xiaohong')
q.test()
>>>The Peerson xiaohong is reading...
重写构造方法
class Person:
def __init__(self,name):
self.name=name
def read(self):
print('The Person '+self.name+' is reading...')
class Student(Person):
def __init__(self,age):
self.age=age
>>>p=Student(12)
>>>p.age
>>>12
>>>p.name
>>>报错,没有name这个属性
- 调用父类的构造函数
class Person:
def __init__(self,name):
self.name=name
def read(self):
print('The Person '+self.name+' is reading...')
class Student(Person):
def __init__(self,name,age):
Person.__init__(name)
self.age=age
>>>p=Student('Hello',12)
>>>p.name
>>>'Hello'
>>>p.age
>>>12
- 使用super函数
class Person:
def __init__(self,name):
self.name=name
def read(self):
print('The Person '+self.name+' is reading...')
class Student(Person):
def __init__(self,name,age):
super().__init__(name)
self.age=age
>>>p=Student('Hello',12)
>>>p.name
>>>'Hello'
>>>p.age
>>>12
子类中添加新方法
class Person:
def __init__(self,name):
self.name=name
def read(self):
print('The Person '+self.name+' is reading...')
class Student(Person):
def __init__(self,name,age):
super().__init__(name)
self.age=age
def getAge(self):
return self.age
>>>p=Student('xiaoming',12)
>>>print(p.getAge())
>>>'xiaoming'
两个重要的内置方法
- isinstance(object,classinfo)如object是classinfo的实例返回True或object是classinfo子类的实例返回True
- issubclass(class,classinfo)如class是classinfo的子类返回True
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类
isinstance(p,Student)#True
isinstance(p,Person)#True
issubclass(Student,Person)#True
issubclass(Person,Student)#False
多继承
class DerivedClassName(Base1,Base2,Base2):
pass
继承也可一级级继承下来,而任何类,最终都可以回溯到根节点object,这些继承关系就像一颗倒挂的树。
子类把父类的所有功能都直接拿过来,这样就不必从零做起
- 子类把父类不适合的方法覆盖(构造函数的重写)
- 若不想子类覆盖父类的方法,将父类方法设为私有
方法 - 子类可新增自己特有的方法
多态
多态:呈现多种形态
- 尽管不知道变量指向的是哪种类型的对象,也能对其进行操作,且操作的行为会随对象所属的类型(类)而异
class Person:
def __init__(self,name):
self.name=name
def getName(self):
return self.name
def read(self):
print('The person is '+self.name+' is reading...')
class Student(Person):
def read(self):
print('The student is '+self.name+' is reading...')
class Teacher(Person):
def read(self):
print('The teacher is '+self.name+' is reading...')
def read_twice(person):
person.read()
person.read()
>>>read_twice(Person('xiaoming'))
>>>The person is xiaoming is reading...
>>>read_twice(Student('xiaoli'))
>>>The student is xiaoli is reading...
>>>read_twice(Teacher('xiaoliu'))
>>>The teacher is xiaoliu is reading...
新增Person的子类,不必对read_twice( ) 进行任何修改,就可以正常运行。
- 实际上,任何依赖Person作为参数的方法和函数
都可以不加修改正常运行,这就是多态 - 由于Person类型有read( )方法,因此,传入的任意
类型,只要是Person类或者子类,就会自动调用
实际类型的read( )方法。
只管调用,不管细节
鸭子类型
静态语言(例如Java),如果需要传入参数是
Person类型,则传入的对象必须是Person类型或者它的子类,否则,将无法调用read( )方法
- Python作为动态语言,则不一定需要传入Person类 型,只需保证传入的对象有一个read( )方法就可以
class Timer:
def read(self):
print("Reading is good!")
read_twice(Timer())
>>>Reading is good!
>>>Reading is good!
动态语言的“鸭子类型”:并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来
像鸭子”,那它就可以被看做是鸭子。
封装
将抽象得到的属性和功能相结合,形成一个有机整体,隐藏对象的属性和实现细节,仅对外提供
公共访问方式的接口
- 封装:无需知道对象的构造
- 多态:无需知道对象的类型,就能调用其方法
通过 类名. 或者 对象名. 形式访问封装内部的属性和方法
class Person:
add='Beijing'
def __init__(self,name):
self.name=name
def read(self):
print('The person '+self.name+'is reading')
>>>p=Person('Hello')
>>>p.add
>>>'Beijing'
>>>p.read()
>>>The person Helli is reading
封装的优点
好处:
- 保护隐私、提高安全性
- 将变化隔离,便于使用
- 提高复用性
模块
模块:Python中,可理解为对应于一个.py文件,将属性变量和实现方法封装在这个文件中
- 任何Python 程序可作为模块导入,通过import 模块名导入(import jieba, import time…)
- 一般用module_name.fun_name、module_name.var_name进行使用(模块名是文件名
去掉.py后缀)
包
包(package): 将多个模块分为一个包
- 包是python模块的有层次的文件目录结构
- 包中包含模块和子包
- 包中必须存在__init__.py文件,包的标识,一般不在
init.py 中写入模块,尽可能保证简单
常见包结构
包中包含多个模块
包中包含多个模块和子包
若main.py要引用package_b中的test_fact模块,导入包
可以使用:
通过模块名.函数名进行引用
from package_b import test_fact
print(test_fact.fact(3))
需要通过完整的名称进行引用
import package_b.test_fact
print(package_b.test_fact.fact(3))
若main.py要引用子包package_a_a中的hello模块,导
入包可以使用:
from package import item 方式导入包时,这个子项(item)既可以是子包也可以是其他命名,如函数、类、变量等
- 例如, from package_a.package_a_a.hello import say_hello
- import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是类、函数、变量等
- 例如,import package_a.package_a_a.hello
- 从* 导入包,利用import *找出包中的所有模块,然后导入
- 在需要导入所有模块的包的__init__.py文件中,定义模糊导入:all=[‘模块1’,‘模块2’,…]
- 慎用 import *
以上是关于python面向对象的主要内容,如果未能解决你的问题,请参考以下文章