python——全面的类学习(1)


在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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方法,如果有则可以运行。换句话说,不管你是不是一只鸭子,如果你有鸭子的特点那么你就是一只鸭子。

猜你喜欢

转载自blog.csdn.net/qq_46126258/article/details/113687382