类的基本使用
class Person(): def __init__(self,name,age): self.name = name self.age = age def info(self): print(f'大家好,我是{self.name},我今年{self.age}岁了') xiaohua = Person('小花',21) xiaohua.info()
打印结果:
大家好,我是小花,我今年21岁了
python里面的类,架子大概就是这个样子,__init__就是构造函数,所有的方法的第一个形参都是self,这个不是关键字,命名为self是行业默认写法,你把self改成abc也行,重点是第一个形参,进来的都是当前实例对象,对标其他编程语言的this。
属性的存取
正常存取
python里面没有私有属性这个东西,默认全部都是public
class Person(): def __init__(self,name,age): self.name = name self.age = age def info(self): print(f'大家好,我是{self.name},我今年{self.age}岁了') xiaohua = Person('小花',21) print(f'年龄是:{xiaohua.age}') xiaohua.age = 22 print(f'改变后的年龄是:{xiaohua.age}')
打印结果:
年龄是:21
改变后的年龄是:22
存取器
python的存取器实际上是用了小花招,它允许你定义属性或者方法的名字前面加上两个下划线,它会对这种格式的名称进行转换,转换后名称也就变了,外部就访问不到该属性,所以就有了私有属性的效果。
class Person(): def __init__(self,name,age): self.__name = name self.__age = age def info(self): print(f'大家好,我是{self.__name},我今年{self.__age}岁了') xiaohua = Person('小花',21) xiaohua.info() print(f'年龄是:{xiaohua.__age}')
xiaohua.info()这一行是可以正常打印出信息,但是下面一行就会报错,报错原因是没有__age这个属性。
我们鉴于这种玩法,就可以做出存取器方法
class Person(): def __init__(self,name,age): self.__name = name self.__age = age def info(self): print(f'大家好,我是{self.__name},我今年{self.__age}岁了') def get_age(self): return self.__age def set_age(self,age): self.__age = age xiaohua = Person('小花',21) xiaohua.info() xiaohua.set_age(23) xiaohua.info()
打印结果:
大家好,我是小花,我今年21岁了
大家好,我是小花,我今年23岁了
但是事实上,私有属性只能成为一种程序员之间的约定,它是拦不住人的,因为python把带两个下划线打头的属性转换后的结果是有规则的,它会转换成一个下划线+类名+原属性名。
class Person(): def __init__(self,name,age): self.__name = name self.__age = age def info(self): print(f'大家好,我是{self.__name},我今年{self.__age}岁了') def get_age(self): return self.__age def set_age(self,age): self.__age = age xiaohua = Person('小花',21) xiaohua.info() xiaohua._Person__age = 22 xiaohua.info()
打印结果:
大家好,我是小花,我今年21岁了
大家好,我是小花,我今年22岁了
特性
在新版的python3中,提供了property函数,可以关联到存取方法,实际上它是一个类,只不过我们用起来是函数的用法。
class Person(): def __init__(self,name,age): self.__name = name self.__age = age def info(self): print(f'大家好,我是{self.__name},我今年{self.__age}岁了') def get_age(self): print(f'这是类里面-我正在读取age的值:{self.__age}') return self.__age def set_age(self,age): print(f'这是类里面-我正在设置age的值:{age}') self.__age = age def del_age(self): print(f'这是类里面-我正在删除age:{self.__age}') del self.__age age = property(get_age,set_age,del_age) xiaohua = Person('小花',21) xiaohua.info() xiaohua.age = 18 xiaohua.info() print(f'我在外面读取age的值:{xiaohua.age}') del xiaohua.age
打印结果:
大家好,我是小花,我今年21岁了
这是类里面-我正在设置age的值:18
大家好,我是小花,我今年18岁了
这是类里面-我正在读取age的值:18
我在外面读取age的值:18
这是类里面-我正在删除age:18
示例中property传入了三个参数,顺序分别是读取方法,设置方法,删除方法,如果都不传,那么该特性将不可读也不可写,传了哪个方法就拥有哪个功能,有第四个可选参数,传入一个文档字符串。
property也可以当成装饰器用
class Person(): def __init__(self,name,age): self.__name = name self.__age = age def info(self): print(f'大家好,我是{self.__name},我今年{self.__age}岁了') @property def age(self): print(f'这是类里面-我正在读取age的值:{self.__age}') return self.__age @age.setter def age(self,age): print(f'这是类里面-我正在设置age的值:{age}') self.__age = age xiaohua = Person('小花',21) xiaohua.info() xiaohua.age = 18 xiaohua.info() print(f'我在外面读取age的值:{xiaohua.age}')
打印结果:
大家好,我是小花,我今年21岁了
这是类里面-我正在设置age的值:18
大家好,我是小花,我今年18岁了
这是类里面-我正在读取age的值:18
我在外面读取age的值:18
继承
常规继承
继承的写法是在类定义的时候类名后面的括号里传入父类名称
class Person(): def info(self): print('人类正在统治世界') class student(Person): def __init__(self): print('我是一名小学生') p = student() p.info()
打印结果:
我是一名小学生
人类正在统治世界
访问父类方法
在python中访问父类方法用一个特殊的函数 : super()
class Person(): def info(self): print('人类正在统治世界') class student(Person): def __init__(self): super().info() print('我是一名小学生') p = student()
打印结果:
人类正在统治世界
我是一名小学生
如果是初始化类的时候要调用父类的构造函数,也是一样的调用方式:
class Person(): def __init__(self,name): self.__name = name def info(self): print(f'人类正在统治世界:{self.__name}') class student(Person): def __init__(self,name): super().__init__(name) p = student('阿西吧') p.info()
打印结果:
人类正在统治世界:阿西吧
方法重写
子类将父类的方法进行重写覆盖
class Person(): def info(self): print('人类正在统治世界') class student(Person): def info(self): print('小学生正在统治世界') p = student() p.info()
打印结果:
小学生正在统治世界
多重继承
多重继承虽然很强大,也很容易成为车祸现场,多个父类中如果有构造函数如果有同名方法都将搞得你头晕脑胀,正常情况下,应该避免使用多重继承,除非忍不住。。。
class Person(): def info(self): print('人类正在统治世界') class Young(): def young_info(self): print('年轻人太天真') class student(Person,Young): def __init__(self): print('小学生正在统治世界') p = student() p.info() p.young_info()
打印结果:
小学生正在统治世界
人类正在统治世界
年轻人太天真
类型校验
class Person(): def info(self): print('人类正在统治世界') class Young(): def young_info(self): print('年轻人太天真') class student(Person,Young): def __init__(self): print('小学生正在统治世界') a = issubclass(student,Person) print(f'是不是子类:{a}') x = student.__bases__ print(f'父类是:{x}') s = student() z = isinstance(s,student) print(f'是不是实例:{z}')
打印结果:
是不是子类:True
父类是:(<class '__main__.Person'>, <class '__main__.Young'>)
小学生正在统治世界
是不是实例:True
是不是实例判断 如果和父类进行判断也算是实例。
抽象接口
面向接口编程的时候都是先定义接口,然后子类去实现接口实现方法,在python3中引入了abc模块,可以帮助我们定义抽象接口
import abc class Person(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass class student(Person): def eat(self): print('小学生也可以吃饭') x = student() x.eat()
打印结果:
小学生也可以吃饭
抽象类中定义的接口在子类中必须实现
静态方法
class student(): remark = '我是祖国的花朵' @classmethod def sing(cls): print('小学生也可以唱歌 ' + cls.remark) @staticmethod def eat(): print('小学生也可以吃饭') student.eat() student.sing()
打印结果:
小学生也可以吃饭
小学生也可以唱歌 我是祖国的花朵
两种装饰器可以定义静态方法,注意区别,@classmethod装饰的方法有默认参数cls,表示当前类,可以取到属性。