一、面向对象
Python也是面向对象的语言,与java一样,面向对象具备三大特点,封装、继承、多态
1.类与对象
- 类是对象的模具,确定对象将会拥有的属性和行为
- 对象是类的具体实例表现
2.封装
封装就是隐藏具体信息及实现细节,提供访问接口
- 特点:
- 只能通过规定的方法访问数据
- 隐藏类的实例细节,方便修改和实现
3.继承
继承是一种类与类之间的关系,使用已经存在的类的定义作为基础来创建新的类(父类与子类),子类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性的继承父类
- 满足
A is a B
的关系就可以形成继承关系 - 特点:
- 利于代码服用
- 便于开发、维护
4.多态
多态意味着允许不同类的对象对同一消息作出不同的相应
- 实现的必要条件:
- 满足继承关系
- 父类引用指向子类对象
二、类的定义及实现
类是面向对象的基础,是数据的一种类型,类的实例称之为对象,类中有很多属性和方法,开发者可以自定义类
1.类的定义(简陋)
class Animal(object):
"""动物类"""
pass
- 每个类都默认继承
object
类,可以不写明,但是规范要求写出来
2.类的特殊方法与属性
在Python中,每个类都有默认的几个特殊方法和属性,可以通过dir(类名)
或者help(类名)
来查看,主要有以下几个
-
构造函数:
def __init__(self, name, *args, **kwargs): self.name = name pass
- 定义类时,如果没有去定义构造函数,Python默认定义一个构造函数
__init__(self)
- 如果定义了构造函数,Python则不会再创建
__init__(self)
- 在定义构造函数时,没有设置参数的默认值在创建实例时必须传入对应的参数,设置了默认值的参数可以不传…另外,设置默认值的参数必须放在必传参数的后面
class Animal(object): def __init__(self, name, age=None): self.name = name self.age = age pass
- 定义类时,如果没有去定义构造函数,Python默认定义一个构造函数
-
析构函数:用于删除类的对象,一般不用去定义,也不会去调用,Python有自己的垃圾处理机制,会自动去管理内存,自动去调用析构函数,这里只稍作了解
def __del__(self):
pass
- 对象描述函数
__str__(self)
:当通过print(obj)
直接打印对象时,会打印出一行对象内存地址相关的字符串,如果想要打出指定的内容,可以定义__str__(self)
函数并返回需要显示的字符串对象,这样直接打印对象时,就会打印出该函数返回的结果 - 常用的特殊属性:
__doc__
:文档信息,注释等内容__module__
:模块信息,注意,如果是调用的是当前模块的类,则会显示__mian__
__bases__
:继承于哪个类__file__
: 文件全路径,经常使用os.path.abspath(__file__)
来获取当前文件全路径- …可以通过
dir(类名)
自行了解
from test_class import Animal print(Animal.__doc__) print(Animal.__module__) print(Animal.__bases__)
3.自定义类的实现
class Cat(object):
"""
自定义动物类
"""
flag = "我是共有属性"
__flag2 = "我是私有的"
_flag3 = "我不能导入"
def __init__(self, name, age):
self.name = name
self.__age = age
def get_flag2(self):
return self.__flag2
def get_age(self):
return self.__age
def set_age(self, age):
self.__age = age
def show_info(self):
info = '我是一只喵,我叫{},{}岁'.format(self.name, self.__age)
print(info)
def __private_func(self):
print("我是私有方法")
@property
def flag3(self):
return self._flag3
- 类变量:Python中,定义在类里面且在该类的方法体外的属性为类属性,可以直接通过该类调用,如
Cat.flag
,也可以通过实例去调用cat.flag
- 通过类对类变量进行赋值时,程序中的所有类变量都会跟着改变,但是如果实例对象已经对该类变量进行过赋值操作,那么会在该实例中生成一个与该类变量同名的实例变量,此时通过类对类变量进行赋值时不会对该实例的此变量有影响
- 通过实例对象对类变量进行赋值,只会对当前实例对象有影响
- 实例变量:定义在构造函数中的属性则为实例属性,可以被继承,外界能通过实例对象直接调用
- 私有变量和方法:Python规定,在类中定义变量或方法时,如果以双下划线开头如
__name
,则该变量或方法为私有变量/方法,外界无法直接访问,子类无法继承,只能通过该类提供的接口来访问该变量 - 禁止导入变量和方法:Python规定,在类中定义变量或方法时,如果以单下划线开头如
_name
,则该变量或方法无法通过from somemodule import *
导入,子类可以继承,外界可以通过实例对象访问
4.类的实例化及判定
- 实例化
cat = Cat('我是小白白', 23)
- 类的实例判断
- 语法:
isinstance(实例名, 类名)
,结果返回布尔值
cat = Cat('我是小白白', 23) print(isinstance(cat, Cat))
- 语法:
5.类实例的扩展
与Java等强类型语言不同,Python可以在运行时对类的实例的属性和函数进行修改、扩展
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
print("我叫{0}, 今年{1}岁".format(self.name, self.age))
def eat():
print("我吃东西")
if __name__ == "__main__":
p = Person('Schuyler', 22)
p.show_info()
p.code = 2222
p.eat = eat
p.eat()
print(p.code)
三、类的继承和多态
class Person(object):
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
def job(self):
print("人类都有自己的工作")
class Student(Person):
"""学生类"""
def job(self):
super(Student, self).job()
print("学生的工作就是读书")
if __name__ == "__main__":
stu = Student("Schuyler", "26")
stu.job()
print(issubclass(Student, Person))
1.继承
- 在Python中,所有的类都默认继承了object类,当需要指定继承某个类的时候,在定义时直接将父类名称作为该类的参数即可,如
class Student(Person)
- 当子类继承父类后将会继承父类的所有方法,和属性,包括构造函数。当然子类可以对方法进行重写
2.调用父类方法
- 语法:
super().methodName()
- 此处不需要传入参数self
- 语法:
super(当前类名, self).methodName()
,与这种方式相比,推荐用第一种,更好维护,比如当前类名改动时,不需要去做过多的修改 - 如果有多个父类,也可以通过
父类名.methodName(self)
来调用,如Person.__init__(self, name)
3.子父类判定-issubclass
- 语法:
issubclass(子类名, 父类名)
,返回布尔值
4.多重继承
- Python中多重继承只需要在定义子类时将多个需要继承的父类名作为子类的形参即可
- 当多重继承时,父类中有存在同名函数时,按形参的排列顺序进行优先调用
class Person(object): def eat(self): print("人需要吃饭") def talk(self): print("人类需要沟通") class Boy(object): def sex(self): print("我是个男的") def talk(self): print("男人之间用拳头交流") class Man(Boy, Person): pass if __name__ == "__main__": man = Man() man.sex() man.eat() man.talk()
5.多态
- 每当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用
- 即同一个方法在不同类中实现不同的功能(函数的重写)
四、类的高级特性
1.@property
- 1.
@property
可以将类的方法当作属性调用即obj.函数名
,这样可以配合私有变量的使用,让私有变量的调用更为便利,灵活 - 2.
@property
可以配合@函数名.setter
注解来对变量的设置进行校验操作,其中函数名指的是@property
注解的函数名 - 3.示例
class Person(object): def __init__(self, name, age): self.name = name self.__age = age @property def age(self): """ 私有变量的访问 """ return self.__age @age.setter def age(self, value): """ 私有变量的设置校验 """ if not isinstance(value, int): print("只能输入数字哦") elif value < 0: print("岁数不能是负数哦") else: self.__age = value if __name__ == "__main__": p = Person("小明", 25) p.age = -1 p.age = "o" p.age = 2 print(p.age)
2.__slots__
- 1.
__slots__
为指定的类设置一个静态属性列表,此时在定义类时,属性的名称需要根据该列表来定义 - 2.
__slots__
为属性很少的类节约内存空间 - 3.
__slots__
的赋值内容是一个元祖数据 - 4.使用
__slots__
后除了元祖数据里指定内容外,不能给实例添加新的属性或者方法(这里解释一下,与Java不同,Python可以在运行过程中对类的实例随意的添加函数和属性,为了防止这一点,可以通过__slots__
来控制) - 5.使用
__slots__
的类的子类也要遵循父类的规则,如果子类需要扩展,可以在子类中也定义__slots__
,这样会在父类的基础上扩充 - 示例
from time import sleep
class Person(object):
__slots__ = ('age', 'name')
def __init__(self, name, age):
self.name = name
self.age = age
def show_info(self):
print("我叫{0}, 今年{1}岁".format(self.name, self.age))
def eat():
print("我吃东西")
if __name__ == "__main__":
p = Person('Schuyler', 22)
p.show_info()
p.name = "小明"
p.age = 11
p.show_info()
#为了方便展示错误信息,这里停顿一秒
sleep(1)
p.code = 2222
p.eat = eat
3.类的静态方法、类方法、实例方法
- 静态方法
@staticmethod
@staticmethod
该注解使得类的方法可以直接通过类来调用,在定义该函数时,无法传入self
class Person(object): def __init__(self, name, age): self.name = name self.age = age def show_info(self): print("我叫{0}, 今年{1}岁".format(self.name, self.age)) @staticmethod def speak(): print("我是个静态方法") if __name__ == "__main__": Person.speak()
2.类方法@classmethod
- 1.使用
@classmethod
注解的方法为类方法,与实例函数区别于传入的不再是类实例(self
)而是类本身(cls
) - 2.类方法可以直接调用类属性,但是无法直接调用实例属性
- 3.类方法虽然无法直接调用实例属性,但是可以通过
cls(...)
来构造该类的实例从而去调用实例属性 - 4.与静态方法类似,类方法也可以直接通过类来调用,但是静态方法无法直接调用类属性
class Person(object):
value = "我是类属性"
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def class_method(cls):
print(cls.value, "名字:{}".format(cls("Schuyler", 11).name))
if __name__ == "__main__":
Person.class_method()