Python学习-面向对象(类)
1 了解面向对象
- Python 就是一个面向对象的编程语言
- 对象就是内存中用来存储指定数据的一块区间
- 对象的结构:每个对象当中都保存了3种数据:
1、id(标识):id是由我们的解析器生成的,在Cython中,id就是对象的内存地址。
2、type(类型):用来标识当前对象所属的类型,类型就决定了对象有什么功能。
3、value(值):就是对象中存储的具体数据。 - 概念:
(1)类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
(2)方法:类中定义的函数。
(3)类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
(4)数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
(5)方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
(6)局部变量:定义在方法中的变量,只作用于当前实例的类。
(7)实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
(8)继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
(9)实例化:创建一个类的实例,类的具体对象。
(10)对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
2 类
2.1 类的引入
- Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
- 对象可以包含任意数量和类型的数据。
- 类(class):简单理解相当于一个图纸,在程序中我们需要根据类来创建对象
- 学习的都是Python的内置对象,如int() str() list() tuple() set() dict()
- 类是type类型的对象,定义类实际上就是定义了一个type类型对象
- 可以通过类来创建一些自定义的对象
- 使用class 关键字来定义类,语法:
class 类名([父类]):代码块
这里类名后面括号里的参数(即父类)可以省略,如果有父类传递过来,那就是继承
- 类名一般采用驼峰命名法
- 现在通过MyClass 创建的对象都是空对象
- 可以向对象中添加变量,对象中添加变量我们称之为属性
语法: 对象.属性名=属性值 - 可以用isinstance()函数来检查一个对象是否是另一个类的实例,返回值是True或False
class MyClass():pass
print(id(MyClass),type(MyClass)) # 35759912 <class 'type'>
mc=MyClass() # mc是通过类 MyClass来创建的,mc就是MyClass的实例
print(mc,type(mc)) # <__main__.MyClass object at 0x00000000028C2780> <class '__main__.MyClass'>
r= isinstance(mc,MyClass)
print(r) # True
mc.name='hello'
print(mc.name) # hello
2.2 类的定义
- 类的定义:类和对象都是现实生活中的抽象
- 实际上,所有的事物有两部分组成:
1.数据(属性)
2.行为(方法) - 在类中的代码块,我们可以定义变量和函数
- 类对象创建后,类命名空间中所有的命名都是有效属性名。
- 在类中定义的变量和函数将会成为所有实例的公共属性和公共方法,任何该类的实例都可以访问。
- 方法调用和函数调用的区别:
1.如果是函数调用,有几个形参,就传递几个实参;
2.如果是方法调用,默认要指定一个形参,所以方法中至少要有一个形参
class Person():a=123b=456def speak(self):print('hahah')
# 创建Person的实例
p1=Person()
# 调用类的属性,语法:对象.属性名
print(p1.a,p1.b) # 123 456
# 调用类的方法,语法:对象.方法名()
p1.speak() # hahah
2.3 属性和方法的查找流程
- 当我们调用一个对象的属性时,解析器会先在当前的对象中寻找是否有该属性(方法):
(1)如果有,则直接返回当前的对象的属性值(方法返回值);
(2)如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值(方法返回值),如果没有就报错。
class Text():c='haha'def spe(self):print('123')
p2=Text()
p2.c='wowo'
print(p2.c) # 结果为 wowo,而不是 haha
- 类对象和实例对象中都可以保存属性(方法),具体要保持到哪里,看实际需求:
(1)如果这个属性(方法)是所以的实例共享的,则应该将其保存到类对象中;
(2)如果这个属性(方法)是某个实例独有的,则应该保存到实例对象中,别的实例访问不到;
(3)一般情况下,属性保存到实例对象中,而方法需要保存到类对象中。
2.4 方法的默认形参 self
- self在定义时需要定义,但是在调用时会自动传入。
- self的名字并不是规定死的,但是最好还是按照约定和规范用 self
- self总是指调用时的类的实例。
- 注意:
(1)类方法必须包含参数 self, 且为第一个参数
(2)在定义类的方法时,不能直接调用方法外的变量,这是和函数嵌套不同的地方。 - 如果要调用,可以使用 self.属性名 的方式
class StrPrint():str='hello'def pri(self):print(self.str)
a=StrPrint()
b=StrPrint()
a.str='hey'
b.str='你好'
# 哪个实例调用,self就是哪个实例的对象
a.pri() # 输出 hey
b.pri() # 输出 你好
# self 代表的是类的实例,代表当前对象的地址
print(a.pri) # 输出 <bound method StrPrint.pri of <__main__.StrPrint object at 0x00000000026D2668>>
2.5 特殊方法
- 在类中可以定义一些特殊的方法,形如__mmm__,注意前后都是双下划线,比如初始化方法:init(self),该方法在类实例化时会自动调用。
- 这些特殊方法不需要我们自己调用,创建实例时就已经调用,有多少个实例就会被调用多少次。
- 类中的代码执行顺序(例如):先执行类里面的代码块,再执行__init__部分的代码块。
- 当一个类创建了多个实例时,类里面的代码只执行一次,而特殊方法则会根据实例的个数执行多次。
class StrPrint():print('hello')def __init__(self):print('hahhah')
p1=StrPrint()
p2=StrPrint()
p3=StrPrint()
执行结果:
hello
hahhah
hahhah
hahhah
- 类的基本结构:
class ClassName([父类]):公共属性1公共属性2……# 对象的初始化方法def __init__(self,...):代码块# 其他的方法def methodname(self,...)代码块……
- 定义一个车的类
属性:name、color
方法:run()
class Car():name=''color=''def __init__(self,name,color):self.name=nameself.color=colordef run(self):print('The car is running.')
car=Car()
3 面向对象的三大特性
- 面向对象的三大特性:
1.封装:确保对象中数据的安全
2.继承:保证对象的扩展性
3.多态:一个对象可以以不同的形态去呈现
3.1 封装
3.1.1 封装的引入
- 出现封装的原因:我们需要一种方式来增强数据的安全性
- 要求:1、属性不能随意修改;2、属性不能改为任意的值
- 封装是指隐藏对象中一些不希望被外部所访问到的属性或方法:
1、将对象属性名修改为一个外部不知道的名字:
class Dog():def __init__(self,name):self.hidden_name=namedef speak(self):print('大家好,我是%s'%self.hidden_name)
d=Dog('大狼狗')
d.name='大柴狗'
d.speak() # 输出结果为:大家好,我是大狼狗 说明类的属性值没有被修改
2、外部使用getter()和setter()方法访问到类的属性并修改:
getter()获取对象中指定的属性
setter()用来设置对象指定的属性
class Dog():def __init__(self,name):self.hidden_name=namedef speak(self):print('大家好,我是%s'%self.hidden_name)# get_name()用来获取类的name属性值def get_name(self):return self.hidden_name# set_name()用来修改类的name属性值def set_name(self,name):self.hidden_name=name
d=Dog('大狼狗')
print(d.get_name()) # 输出:大狼狗
d.set_name('二哈')
print(d.get_name()) # 输出:二哈
d.speak() # 输出:大家好,我是二哈
3.1.2 封装的作用
- 使用封装,确实增加了类的定义的复杂程度,但也确保了数据的安全:
(1)隐藏属性名,是调用者无法随意修改类的属性。
(2)增加getter()和setter()方法,控制属性是否是只读的。
(3)使用setter()设置属性,可以验证数据的准确性。
(4)使用getter()方法获取属性,使用setter()方法设置属性可以在读取属性和修改属性的同时做一些其他的处理。
class Dog():def __init__(self,name,age):self.hidden_name=nameself.hidden_age=agedef speak(self):print('大家好,我是%s'%self.hidden_name)# get_name()用来获取类的name属性值def get_name(self):return self.hidden_name# set_name()用来修改类的name属性值def set_name(self,name):self.hidden_name=namedef get_age(self):print('用户读取了属性...')return self.hidden_agedef set_age(self,age):print('用户修改了属性。。。')if age > 0:self.hidden_age= age
d=Dog('大狼狗',2)
d.get_age()
d.set_age(3)
3.1.3 基本属性、隐藏属性、私有属性
- 其属性名不包含任何下划线的属性为基本属性。
- 双下划线开头的属性,形如__***,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问。
- 其实隐藏属性只不过是Python自动为属性改了一个名字 --> _类名__属性名,例如 __name --> _Person__name,然而这种方式实际上依然可以在外部访问,所以这种方式我们一般不用。因此我们使用单个下划线_开头定义私有属性,一般情况下,使用_开头的属性都是私有属性,没有特殊情况下不要修改私有属性。
示例1:
class Person():def __init__(self,name):# 定义隐藏属性self.__name=namedef get_name(self):return self.__namedef set_name(self,name):self.__name=name
p1=Person('张三')
# p1.__name 会报错 AttributeError: 'Person' object has no attribute '__name',隐藏属性只能在类的内部访问,无法通过对象访问。
print(p1._Person__name) # 通过这种方式依然可以获取类的属性,输出结果为:张三
示例2:
class Person():def __init__(self,name):# 定义私有属性self._name=namedef get_name(self):return self._namedef set_name(self,name):self._name=name
p2=Person('李四')
# print(p2._Person_name) # 访问不了,会报错AttributeError: 'Person' object has no attribute '_Person_name'
print(p2._name) # 访问私有属性,输出结果为:李四,没有特殊情况下不要修改私有属性
3.1.4 类的装饰器
- 类的装饰器:扩展方法的功能
- property装饰器:
(1)我们可以使用 @property装饰器来创建只读属性。
(2)@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
(3)@property装饰器应用场景:当定义的方法内容比较庞大,且不需要传递参数,同时方法只需要一个返回值的时候。
class Person():def __init__(self,name):self._name=name# getter()方法@propertydef get_name(self):print('getter()方法执行了')return self._name# getter 和 setter 方法的定义: @方法名.getter 和 @属性名.setter
p3=Person('王五')
# 调用Person类的get_name()方法,虽然是在调用方法,但加了@property装饰器之后,方法已经转换为属性,访问时方法名后面不需要加括号
print(p3.get_name) # 输出结果为: getter()方法执行了 王五
3.2 继承
- 继承:子类会继承父类的所有属性和方法,包括特殊方法。
- Python同样支持类的继承,如果一种语言不支持继承,类就没有什么意义了。类提高了代码的复用性。
- 在创建类的时候,如果省略了父类,则默认父类为object,object是所有类的父类。
- 基类(BaseClassName)必须与派生类定义在一个作用域内。
- super()函数可以获取当前类的父类,并且通过super()返回对象调用父类方法时,不需要传递self。
3.2.1 单继承
派生类(derivelclass)的定义语法如下:
class DerivelclassName(BaseClassName1):代码块
除了类,还可以用表达式,基类定义在另一个模块中时,这一点非常有用:
class DerivedClassName(modname.BaseClassName):
# 定义基类
class PersonInformation():# 定义基本属性name=''age=0# 以单下划线开头,定义私有属性,私有属性在类外部无法直接进行访问_weight =0# 定义初始化方法(构造方法)def __init__(self,name,age,weight):self.name= nameself.age=ageself._weight=weightdef print_infor(self):print('name:%s age:%d weight:%d'%(self.name,self.age,self._weight))
# 单继承示例:
class PersonOne(PersonInformation):# 添加新的基础属性city=''def __init__(self,name,age,weight,city):调用父类的构造方法,PersonInformation.__init__(self,name,age,weight) # 等价于super().__init__(name,age,weight)self.city=city# 重写父类的方法def print_infor(self):print('name:%s age:%d weight:%d city:%s'%(self.name,self.age,self._weight,self.city))
p1=PersonOne('李四',23,55,'hangzhou')
p1.print_infor() # 输出结果为 name:李四 age:23 weight:55 city:hangzhou
issubclass(子类名,父类名)函数:检查一个类是不是另一个类的子类,如果是 则返回True,如果不是 则返回false
print(issubclass(PersonOne,PersonInformation)) # 输出:True
print(issubclass(PersonOne,object)) # 输出:True
print(issubclass(PersonInformation,object)) # 输出:True
3.2.2 多继承
- Python同样有限的支持多继承形式。多继承的类定义形如:
class DerivedClassName(Base1,Base2,Base3,...):代码块
- 注意:圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
# 定义另一个基类
class JobInformation():job=''_salary=0def __init__(self,job,salary):self.job=jobself._salary=salarydef print_infor(self):print('job:%s salary:%d'%(self.job,self._salary))
# 多重继承示例:
class PersonTwo(JobInformation,PersonInformation):def _init__(self,name,age,weight,job,salary):# 调用父类的构造方法PersonInformation.__init__(self,name,age,weight)JobInformation.__init__(self,job,salary)
p2=PersonTwo('teacher',6000)
p2.print_infor() # 方法名同,默认调用的是在括号中排前地父类的方法,输出结果为: job:teacher salary:6000
# 类名.__bases__ 这个属性可以获取当前类的所有父类
print(PersonTwo.__bases__) # 输出结果: (<class '__main__.JobInformation'>, <class '__main__.PersonInformation'>)
3.2.3 方法重写
- 如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法。
- 如果在子类中有何父类重名的方法,则通过子类的实例去调用该方法时,会先调用子类的方法,这个特点我们称之为方法的重写(覆盖)。
- 方法的查找顺序:先在子类找,没有找到则去继承的父类找,以此类催,直到找到object,如果依然没有找到则会报错。
# 定义父类
class Parent():def myMethod(self):print('调用父类方法')
# 定义子类
class Child(Parent):def myMethod(self):print('调用子类方法')
# 创建子类实例
c=Child()
# 子类调用重写方法
c.myMethod() # 输出结果为: 调用子类方法
3.3 多态
- 例如:len()函数,len()函数的参数可以是列表、字符串等等,其用法就是一种多态的表现。
- len()函数之所以可以获取不同对象的长度,是因为对象中有一个特殊方法 len
4 属性和方法(总结)
4.1 实例属性和类属性
- 实例属性:通过实例对象添加的属性属于实例属性
- 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
- 类属性:直接在类中定义的属性是类属性.
- 类属性可以通过类或类的实例访问到。但是类属性只能通过类对象来修改,无法通过实例对象修改。
class ClassName():# 定义类属性num=10
a=ClassName()
# 定义实例属性
a.str='haha'
a.num=20
print('类的num属性值:',ClassName.num) # 输出结果: 类的num属性值: 10
print('实例的num属性值:',a.num) # 实例的num属性值: 20
print('实例的str属性值:',a.str) # 实例的str属性值: haha
ClassName.num=30
print('类的num属性值:',ClassName.num) # 输出结果: 类的num属性值: 30
print('实例的num属性值:',a.num) # 实例的num属性值: 20
4.2 实例方法和类方法
- 实例方法:在类中定义,以self为第一个参数的方法都是实例方法。
- 实例方法在调用时,Python会将调用对象以self传入。
- 实例方法可以通过类实例和类去调用:
(1)当通过实例调用时,会自动将当前调用对象作为self传入。
(2)当通过类调用时,不会自动传递self,我们必须手动传递当前实例对象。 - 类方法:在类的内容以@classmethod 来修饰的方法属于类方法。
- 类方法第一个参数是cls,也会自动被传递。cls就是当前的类对象。
- 类方法和实例方法的区别:实例方法的第一个参数是self,类方法的第一个参数是cls。
- 类方法可以通过类去调用,也可以通过实例调用。
class ClassName():# 定义类属性num=10# 定义实例方法,第一个参数为self,会自动传入def test(self):print('这是实例方法。')# 定义类方法@classmethoddef test1(cls):print('这是类方法。')# 类方法和实例方法调用属性的方式差不多print(cls.num)
a=ClassName()
# a.test() 等价于 ClassName.test(a)
a.test() # 输出结果为:这是实例方法。
ClassName.test(a) # 括号里的a是当前的实例对象,输出结果为:这是实例方法。
# a.test1() 等价于 ClassName.test1()
a.test1() # 输出结果为:这是类方法。
ClassName.test1() # 输出结果为:这是类方法。
4.3 静态方法
- 静态方法:在类中用@staticmethod来修饰的方法属于静态方法。
- 静态方法不需要指定任何的默认参数,并且静态方法可以通过类和实例调用。
- 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数。
- 静态方法一般都是些工具方法,和当前类无关。
class A():@staticmethod# 静态方法需要指定任何的默认参数,包括实例方法的self和类方法的clsdef statmeth():print('这是静态方法。')
a=A()
# 以下两种调用方式的输出结果均为: 这是静态方法。
a.statmeth()
A.statmeth()
4.4 类的私有方法和专有方法
- __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。
调用方式:self.__private_methods
示例:
class Per():def __init__(self,name,age):self.name=name # 公共属性self.__age=age # 私有属性def public(self): # 公共方法print('这是公共方法。')def __private(self):print('这是私有方法。')
p=Per('张三',23)
p.public() # 正常输出
p.__private() # 会报错 AttributeError: 'Per' object has no attribute '__private'
- 类的专有方法:
init : 构造函数,在生成对象时调用
del : 析构函数,释放对象时使用
repr : 打印,转换
setitem : 按照索引赋值
getitem: 按照索引获取值
len: 获得长度
cmp: 比较运算
call: 函数调用
add: 加运算
sub: 减运算
mul: 乘运算
truediv: 除运算
mod: 求余运算
pow: 乘方
4.5 运算符重载
Python同样支持运算符重载,我们可以对类的专有方法进行重载,实例如下:
class Vector:def __init__(self, a, b):self.a = aself.b = bdef __str__(self):return 'Vector (%d, %d)' % (self.a, self.b)def __add__(self, other):return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
# 相加过程: Vertor( 2+5 , 10+(-2) )
print(v1 + v2) # 输出结果为:Vector (7, 8)
Python学习-面向对象(类)相关推荐
- Python学习 - 面向对象之多态
Python学习 - 语法入门:https://blog.csdn.net/wanzijy/article/details/125287855 Python学习 - 数据类型:https://blog ...
- Python之面向对象类和对象
Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...
- python学习------面向对象的程序设计
一 面向对象的程序设计的由来 1940年以前:面向机器最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数 据.简单来说,就是直接编写 0 和 1 的序列来代表程 ...
- python学习面向对象_Python小白必学的面向对象
我们已经知道在Python中"一切皆对象",每个对象都有特定的类型,现在让我们来尝试创建自己的类型--这需要使用class关键字来定义新的"类"(Class), ...
- Python学习笔记④——类、面向对象的三大特征 [封装+继承+多态]
✅ Blogger Learns Python is for learning the "Deep Learning". 文章目录 一.面向对象编程的简介 二.类与对象 -- 基础 ...
- Python学习---面向对象的学习[深入]
类的深入学习 a. Python中一切事物都是对象 b. class Foo: pass obj = Foo() # ...
- python学习面向对象Day11(P116~~126)
b站达内python课学习笔记 P116 Python面向对象–3.1 课程回顾 一.day10复习 """day10 复习类和对象类:抽象 向量 class Vecto ...
- python学习面向对象Day09(P96~~106)
b站达内python课学习笔记 P96~97 Python面向对象-1.1&1.2 课程内容回顾01&02 一.Day08复习 """day08 复习函数 ...
- python游戏房间_使用 Python 学习面向对象的编程 | Linux 中国
使用 Python 类使你的代码变得更加模块化.-- Seth Kenlon 在我上一篇文章中,我解释了如何通过使用函数.创建模块或者两者一起来使 Python 代码更加模块化.函数对于避免重复多次使 ...
最新文章
- XamarinAndroid组件教程RecylerView适配器设置动画
- 客服机器人源码_快速搭建对话机器人,就用这一招!
- [libjingle学习笔记]编译注意事项
- 使用Disentangling形式的损失函数回归2D和3D目标框
- 运行Java应用必须通过main()方法吗?
- dom4j解析xml获取所有的子节点并放入map中
- asp.net调用前台js调用后台代码分享
- 【jquery】fancybox 是一款优秀的 jquery 弹出层展示插件
- 同花顺开放接口api_接口大师,即刻构建你的OpenAPI+开放平台
- android-常用部件
- php对字符数组进行排序,php数组去重_php对数组中字符串去重并排序例子
- SPSS多元线性回归及逐步回归学习笔记
- 【修真院“正直”系列之三】【修真神界】【修行卷】【第一章】修真院入场券...
- java socket连接超时_Java中Socket设置连接超时的代码
- JDK 动态代理之源码解析
- 镭速介绍关于高速数据传输!
- 神经网络模型画图工具,神经网络模型图怎么画
- wps两列数据分别作为xy轴_一图胜千字:科研论文插图/数据处理和图表制作学习会(2019年12月2729日 上海)...
- 深度学习之格式转换笔记(三):keras(.hdf5)模型转TensorFlow(.pb) 转TensorRT(.uff)格式
- php开发多用户商城用什么技术,php多用户商城系统有什么特点?