1、定义类:
class Student:
# 类属性
native_place = "黑龙江"
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 实例方法
def eat(self):
print(self.name + '在吃饭')
# 静态方法
@staticmethod
def method():
print("我是静态方法")
# 类方法
@classmethod
def cm(cls):
print("我是类方法,调用时cls不需要进行传入")
def在class外部叫做 “ 函数 ” ,def在class内部叫做 “ 实例方法 ”
2、实例对象的创建
有了实例就可以调用该类的内容,每个实例都有一个类指针指向类别(格式为:对象名.方法名)
stu1 = Student("张三", 20) #创建实例
stu1.eat() #调用实例方法
print(stu1.name + ' is ' + str(stu1.age) + ' years old')
'''
张三在吃饭
张三 is 20 years old
'''
下面这种形式也可以实现同样的功能(格式为:类名.方法名(类的对象),此时类的对象就是方法的self)
print("-----------------------")
Student.eat(stu1)
'''
-----------------------
张三在吃饭
'''
3、类属性
- 类属性:定义在方法外的变量,会被该类的所有对象共享
- 类属性的使用方法:(1)类名.类属性(2)对象.类属性
stu1 = Student("张三", 20)
stu2 = Student("李四", 22)
print(Student.native_place)
print(stu1.native_place)
print(stu2.native_place)
'''
黑龙江
黑龙江
黑龙江
'''
在父类中的类属性发生改变时,stu1、stu2的类指针不变,但由于父类共享的类属性发生变化,所以stu1、stu2的类属性也会相应改变。
Student.native_place = '天津'
print(stu1.native_place)
print(stu2.native_place)
'''
天津
天津
'''
4、类方法、静态方法
- 类方法:使用
@classmethod
修饰的方法,实用类名直接访问。 - 实例方法传的是实例对象self,类方法传递的是cls(class)
- 类方法的使用方式如下:
Student.cm()
'''
我是类方法,调用时cls不需要进行传入
'''
- 静态方法:使用
@staticmethod
修饰的方法,实用类名直接访问 - 与类方法、实例方法不同的是他没有默认参数
Student.method()
'''
我是静态方法
'''
5、动态绑定属性和方法
- 动态绑定只适用于当前的实例对象并不影响到类
(1)在类的属性确定的情况下,给实例对象绑定新的属性
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(self.name + '在吃饭')
stu1 = Student("张三", 20)
stu1.gender = '女'
print(stu1.name, stu1.age, stu1.gender)
'''
张三 20 女
张三在吃饭
'''
(2)在类的方法确定的情况下,给实例对象绑定新的方法
def show():
print("定义在类方法以外的称之为函数")
stu1.show = show
stu1.show()
'''
定义在类方法以外的称之为函数
'''
6、面向对象的三大特征
(1)封装:提高程序的安全性。
- 将属性和方法包装到类对象中,在方法内部对属性进行操作,在类对象外部调用方法。进而无需关心方法内部具体实现细节,隔离复杂度。
- 在python中没有专门修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前面使用两个"_"。(例子如下)
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age
def show(self):
print(self.name, self.__age)
stu1 = Student("张三", 20)
print(stu1.name)
print(stu.__age)
'''
print(stu.__age)
NameError: name 'stu' is not defined
张三
'''
这时候报错,我们无法直接读取age的信息,但是也可以通过另一种方式进行读取,查看dir如下:
print(dir(stu1))
'''
['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'show']
找到了第一个的’_Student__age’,倒数两个的’name’, ‘show’,那么说明我们隐藏的age属性被改名为’_Student__age’
print(stu1._Student__age)
'''
20
'''
事实上这虽然看上去没什么卵用,但实际上是通过代码人员的自觉性而设置的一道隐私防线,当看到"__"时就不应该访问其中的信息了。
(2)继承:提高代码的复用性
- python支持多继承(继承多个父类)
- 如果一个类没有继承任何一个类,那么默认它的父类是object(相当于没有)
- 定义子类时,必须在其构造函数中调用父类的继承函数(对于父类的所有属性都要继承,否则报错)例子如下:
class Person(object): # 定义父类
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name, self.age)
class Student(Person): #Student继承父类属性的name、age和父类方法
def __init__(self, name, age, stu_no):
super().__init__(name, age) #继承初始化
self.stu_no = stu_no
class Teacher(Person):#Teacher继承父类属性的name、age和父类方法
def __init__(self, name, age, teachofyear):
super().__init__(name, age)
self.teachofyear = teachofyear
stu = Student("zhangsan", 20, 123456)
teacher = Teacher("lisi", 30, 5)
stu.info()
teacher.info()
'''
zhangsan 20
lisi 30
'''
这时我们会面临一个问题,使用info输出name、age时,无法输出子类特有的属性,这时候如果直接在子类中直接对info改写会覆盖掉父类中输出name、age的方法,所以我们可以先调用info,再对info进行改写(又称方法重写),方法重写不会改变实例对象的类型。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name, self.age)
class Student(Person):
def __init__(self, name, age, stu_no):
super().__init__(name, age)
self.stu_no = stu_no
def info(self):
super().info() # 或者 super(Student, self).info()
print(stu.stu_no)
stu = Student("zhangsan", 20, 123456)
stu.info()
'''
zhangsan 20
123456
'''
object类
- 当一个类没有继承任何一个父类的时候,默认继承object类,也就是说所有的类都继承了object类的属性和方法,父类object包含多个方法。
- 使用内置函数dir()可以查看指定对象所有的属性
- object类有一个__str__()方法,用于返回“对象的描述”,str()方法常和print()结合在一起查看对象信息
- 正常直接输出
print(stu)
会得到stu的地址,但是通过object的__str__()方法可以修改print(stu)
的返回值(如下)
也就是说对默认父类也可以进行方法重写!
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): #这里开始对父类的方法进行方法重写
return "我的名字是{0},今年{1}岁了".format(self.name, self.age)
stu = Student("zhangsan", 20)
print(stu)
'''
我的名字是zhangsan,今年20岁了
'''
(3)多态:提高程序的可扩展性、可维护性
- 简单来说就是“具有多种形态”,即便不知道一个变量引用的对象是什么类型,仍可通过变量调用方法,在运行过程中根据变量引用对象的类型,动态决定调用哪个方法
- 这里就要提到“动态语言”,python是一门动态语言可以随时随地绑定新的属性和方法。动态语言崇尚的是“鸭子类型”即无论自身是什么类型如果行为是符合鸭子,那么他就是一只鸭子
- 相对应的“静态语言”:例如java,如果想实现多态要有三个必要条件(1)继承(2)方法重写(3)父类引用指向子类对象
class Animal():
def eat(self):
print('动物会吃')
class Dog(Animal):
def eat(self):
print('狗吃骨头')
class Cat(Animal):
def eat(self):
print('猫吃鱼')
class Person:
def eat(self):
print('人吃五谷杂粮')
def fun(obj):
obj.eat()
fun(Dog()) # 重写了Animal的eat方法
fun(Cat())
fun(Animal())
print(---------------)
fun(Person())
'''
狗吃骨头
猫吃鱼
动物会吃
人吃五谷杂粮
'''
在这个例子中的各个类有下图的继承关系:
在调用fun()函数时我们忽略了调用对象的类型(继承关系),只关注其其是否具有eat方法,如果有则可以运行。换句话说,不管你是不是一只鸭子,如果你有鸭子的特点那么你就是一只鸭子。