一、什么是类?什么是对象?
Python当中一切皆对象,类是对象的抽象,但类也是对象,叫做类对象,而对象是类的实例,比如猫是类,狗是类,而一只布偶猫是对象,一只二哈是对象(注意我使用了一只,代表实例)
举个例子:“人”就是一个类,其中每一个具体的人,比如你,比如我或者其他的每一个人,这都是“人”这个类的对象;
再举个例子:我说“手机”,你能想到很多手机,小米的手机,华为的手机,你的手机,我的手机等等很多手机,所以当我说“手机”的时候,他就是一个类别。
二、类的创建和对象的创建
class Student: # 创建类
def __init__(self, name, age): # 创建类的方法,并且使用__init__初始化了了两个属性
self.name = name # 进行属性的实例化
self.age = age
@classmethod # 类方法,因为使用了classmethod进行了修饰
def cm(cls):
print("学生在操场")
@staticmethod # 静态方法,因为使用了staticmethod进行了修饰
def stm():
print("学生在上课")
# 实例方法,里面有个默认参数self表示类的对象
def greet(self):
print("你好")
def eat(self):
print('学生在吃饭')
stu1 = Student('小明', 19) # 创建了Student类的对象
stu2 = Student('李华', 18) # 创建了Student类的对象
print(stu1) # 输出了类的对象在内存中的地址
print(stu1.name) # 有了类的对象,才可以进行类属性的调用
stu1.eat() # 类的对象名.方法名
stu1.cm()
Student.eat(stu1) # 类名.方法名(类的对象) --->实际上就是方法定义处的self
stu1.eat()
Student.greet(stu1)
stu1.greet()
Student.cm() # 调用classmethod方法时,括号里不用写类的对象
print("------------为stu1动态绑定属性---------------")
print(stu1.name, stu1.age)
stu1.gender = '女' # 直接用对象去添加一个属性并赋值就可以了
print(stu1.name, stu1.age, stu1.gender)
# print(stu2.name, stu2.age, stu2.gender) # 如果该对象没有绑定属性而去使用该属性的话,会报错;因为它是在创建对象stu1后单独进行绑定的
print("------------------------------------------")
stu1.eat()
stu2.eat() # 因为eat()方法时定义在Student类中的,作为类的对象stu1,stu2当然都可以进行调用
print("------------动态绑定方法---------------")
def show():
print("定义在类之外,称为函数")
stu1.show = show
stu1.show()
stu2.show() # stu2没有绑定show方法,使用的时候会报错
__init__ 是 Python 中的特殊方法(special method),它用于对对象进行初始 化,类似于 C++ 中的构造函数;
greet 是我们自定义的方法。
在Python中定义类经常会用到__init__函数(方法),首先需要理解的是,两个下划线开头的函数是声明该属性为私有,不能在类的外部被使用或访问。而__init__函数(方法)支持带参数类的初始化,也可为声明该类的属性(类中的变量)。__init__函数(方法)的第一个参数必须为self,后续参数为自己定义。
————————————————
版权声明:本文为CSDN博主「luzhan66」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luzhan66/article/details/82822896
完整的使用类的步骤:
①创建一个类
class Student:
②定义类的方法
1、
def __init__(self, name, age): # 使用__init__初始化了带两个属性的类的方法
2、使用@calssmethod 创建类的方法
@classmethod # 类方法,因为使用了classmethod进行了修饰
def cm(cls):
print("学生在操场")
3、使用@staticmethod创建类的静态方法,也就是该方法不能携带参数
@staticmethod # 静态方法,因为使用了staticmethod进行了修饰
def stm():
print("学生在上课")
③类的方法中有属性的话,要进行属性的实例化
self.name = name
self.age = age
④创建类的对象
stu1 = Student('xiao', 19) # 创建了Student类的对象,并传入了两个实参
⑤进行类的属性的调用
1、stu1.age #进行属性的调用,注意一定要使用print进行打印
⑥进行类的方法的调用
1、stu1.stm() #进行方法的调用,类的对象名.方法名 ;
2、Student.eat(stu1) # 类名.方法名(类的对象) --->实际上就是方法定义处的self
三、面向对象的三大特征
面向对象的三大特征
封装:提高程序的安全性
将数据 (属性)和行为 (方法) 包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个“_”
继承:提高代码的复用性
多态:提高程序的可扩展性和可维护性
封装
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age # age不希望在类的外部被调用,所以加了俩个__
def show(self):
print(self.name, self.__age) # show()在Student类的内部所以可以调用__age
stu = Student("张三", 19)
stu.show()
# 在类的外部使用name属性与age属性
print(stu.name)
# print(stu.__age) #它 不在类的内部输出,所以会报错
# 那如何在类的外部使用?
print(dir(stu))
可以在输出结果中找到_Student__age
print(stu._Student__age) # 在类的外部可以通过_Student__age进行访问
可以看到,年龄被调用成功了
那可能有人会问,那使用_ _还有什么意义,这就看个人的自觉性了,看到_ _属性就别访问了
继承
单继承
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, num):
super().__init__(name, age)
self.num = num
class Teacher(Person):
def __init__(self, name, age, teachofyear):
super().__init__(name, age)
self.teachofyear = teachofyear
stu = Student("学生", 17, '18')
teach = Teacher("老师", 34, "4")
stu.info()
teach.info()
teach.info()
super()是调用父类的一种方法;在子类中,可以通过super()来调用父类的方法
(假如 C类继承B类,B类由继承A类,那么A类就是C类的超类)
因为继承需要用到父类的属性所以子类必须调用父类的构造方法;
多继承
# 多继承
class A(object): # A继承object类
pass
class B(object): # B继承object类
pass
class C(A, B): # C继承A类和B类,所以C有两个父类,所以C就是多继承
pass
多态
简单地说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型动态决定调用哪个对象中的方法。
class Animal(object):
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() # 然后调用这个对象的eat()方法
# 开始调用函数
fun(Cat()) # Cat加()表示是Cat类的对象的实例化
fun(Dog()) # Cat类,Dog类继承了父类Animal,Cat类;
# Dog类中的eat()方法实际上是重写了父类Animal中的eat()方法,
# 所以在进行调用的时候会调用自己重写过的eat()方法
fun(Animal())
fun(Person()) # 而Person类和Animal类不存在继承关系,
# 但是Person类有eat()方法,
# 所以也会调用Person类中的eat()方法
print(type(Person()))
解决了,那个方法传入一个对象,哪个对象也继承了object类,它会去找这个对象下的我们写的那个方法,只要有它就会去调用,没有的就会报“在这个对象中找不到我们写的那个方法”;
六、特殊方法和特殊属性
什么是特殊的方法和属性?由两个"_"开始或结束的方法和属性;
特殊属性
class A:
pass
class B:
pass
class C(A, B):
def __init__(self, name, age):
self.name = name
self.age = age
x = C("Jack", 19)
print(x.__dict__) # 实例对象的属性字典
print(C.__dict__) # 类对象的属性和方法的字典
print("-----------------------------")
print(x.__class__) # <class '__main__.C'> 输出对象所属的类
print("-----------------------------")
print(C.__bases__) # C类的父类类型的元素
print("-----------------------------")
print(C.__base__) # 输出C类的基类,也就是和它离得最近的父类
print("-----------------------------")
print(C.__mro__) # 类的层次结构
print("-----------------------------")
print(A.__subclasses__()) # 查看A类的子类的列表
特殊方法
__add__
class Student:
def __init__(self, name):
self.name = name
stu1 = Student("张三")
stu2 = Student("李四")
s = stu1 + stu2
print(s)
直接相加会报错,显示不支持“Student”与"Student"相加;
那我非得让他们两个相加呢?
class Student:
def __init__(self, name):
self.name = name
def __add__(self, other):
return self.name + other.name
stu1 = Student("张三")
stu2 = Student("李四")
s = stu1 + stu2 # 实现了两个对象的加法运算,因为在Student类中,编写了__add__()特殊 方法
print(s)
s1 = stu1.__add__(stu2)
print(s1)
想让两个对象实现相加必须编写__add__()特殊方法
__len__
class Student:
def __init__(self, name):
self.name = name
def __add__(self, other):
return self.name + other.name
def __len__(self):
return len(self.name)
stu1 = Student("张三")
stu2 = Student("JACK")
lst = [11, 22, 33]
print(len(lst)) # 内置函数len(),实现了计算列表的长度
print("-------------------------")
print(len(stu1))
print(stu2.__len__()) # 这俩写法效果一样
四、方法重写
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 __int__(self, name, age, num):
super().__init(name, age)
self.num = num
class Teacher(Person):
def __int__(self, name, age, teachofyear):
super().__init(name, age)
self.teachofyear = teachofyear
stu = Student("学生", 17)
teach = Teacher("老师", 34)
stu.info()
teach.info()
例如上面这个代码,Student类和Teacher类都继承了Person类中的name和age属性,虽然我在Student类中还定义了一个属性num,在Teacher类中还定义了一个属性teachofyear,但是因为调用的时Person类中的info()方法,所以不能输入num的实参和teachofyear的实参,因为父类Person中的info()方法中没有;
当我们想输出子类中独有的东西时,子类就要用到方法重写;
如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写
子类重写后的方法中可以通过super().xxx()调用父类中被重写的法
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, num):
super().__init__(name, age)
self.num = num
def info(self): # 在Student子类中重写父类Person中的info方法
super().info() # 如果不写这一行,直接用Student类的对象调用info方法时,只会输出传入num的实参,
# 因为此时info方法已经重写了;
# 因此想要输出name和age需要使用super()对父类中的info()进行调用
print(self.num) # 在父类Person中的info方法中添加一个输出num属性
class Teacher(Person):
def __init__(self, name, age, teachofyear):
super().__init__(name, age)
self.teachofyear = teachofyear
stu = Student("学生", 17, '18')
teach = Teacher("老师", 34, "4")
stu.info()
teach.info()
把鼠标放在Person类中的info()方法前的箭头上会显示“在Student类中被重写过”
五、object类
object类是所有类的父类,因此所有类都有object类的属性和方法。
内置函数dir()可以查看指定对象所属性
Object有一个 _str_()方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对_str_()进行重写
class Student: #他没有继承任何类的话会默认继承object类
pass
stu = Student()
print(dir(stu)) #使用object类中的dir()方法查看stu这个对象所具有的属性和方法
可以看到有很多方法和属性,那这些方法和属性都是Student类中定义的吗?当然不是,它是从父类objec当中继承过来的
class Student: # 他没有继承任何类的话会默认继承object类
pass
stu = Student()
# print(dir(stu)) # 使用object类中的dir()方法查看stu这个对象所具有的属性和方法
print(stu) # 默认调用__str__()方法
使用str(类的对象)输出的是类的对象也就是stu在内存中的地址
我们对__str__()方法进行重写
class Student: # 他没有继承任何类的话会默认继承object类
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"我的名字是{self.name},我今年{self.age}"
stu = Student("李华", 19)
# print(dir(stu)) # 使用object类中的dir()方法查看stu这个对象所具有的属性和方法
print(stu) # 默认调用__str__()方法
print(type(stu)) # stu是Student类的对象,所以他的类型也是Student
重写完__str__方法后,执行print(str(stu))你会发现它不在输出stu的内存地址,而是调用__str__方法,输出里面的内容