1 类方法和静态方法
类中的方法分为:对象方法,类方法和静态方法。
1.1 对象方法
1.直接声明在类中;
2.有默认参数self;
3.通过对象去调用。
1.2 类方法
1.在声明前添加@classmethod
2.有默认参数cls,调用的时候不需要给cls传参。系统会自动将调用当前类方法的类传给cls;cls最终指向的是一个类,类可以做的事情cls都可以做
3.通过类去调用: 类.类方法()
1.3 静态方法
1.在声明前添加staticmethod
2.没有默认参数
3.通过类去调用:类.静态方法()
1.4 对象方法、类方法和静态方法的选择
1.4.1什么时候使用对象方法:
当实现函数的功能需要用到对象的属性的时候就使用对象方法.
1.4.2什么时候使用类方法:
实现函数的功能不需要对象的属性,但是需要类(类的字段,创建对象等)的时候就使用类方法.
1.4.3什么时候使用静态方法
实现函数的功能既不需要对象的属性,也不需要类的时候,就使用静态方法.
# 创建一个数学类,属性:pi;功能:求两个数的和,和圆的面积
import math
class Math:
pi=math.pi
@staticmethod
def my_sum(num1,num2):
return num1+num2
@classmethod
def circle(cls, r):
return cls.pi * r ** 2
print(Math.my_sum(2,3))
print(Math.circle(2))
# 类方法
@classmethod
def destroy(cls):
print('cls:',cls) # cls: <class '__main__.Person'>
print('人类破坏环境')
# 可以用cls来创建对象
p1=cls('小明')
p1.eat('面包') # 小明在吃面包
# 可以用cls使用类的字段
print(cls.num) # 61
# 可以用cls调用类相关的方法
# 调用类方法
Person.destroy()
2.私有化
2.1 私有化
在类中,可以通过在属性名前,或者方法名前加__(注意:不能以__结尾),那么这个属性就会变成私有的;私有的属性和方法在类的外部不能使用。
class Person:
__num=100 # 私有的属性
def __init__(self):
self.name='张三'
def __show_message(self):
print(Person.__num)
print('名字:',self.name)
2.2 私有化原理
python中没有真正的私有化,不能从访问权限上控制属性和方法的使用;只是在名字前有__但是没有以__结尾的名字前再加了'_类名',导致不能直接通过原属性和方法名进行访问。
class Dog:
def __init__(self,name,color):
self.__name=name
self.__color=color
dog1=Dog('大黄','黄色')
# print(dog1.__name) # AttributeError
print(dog1.__dict__) # {'_Dog__name': '大黄', '_Dog__color': '黄色'}
#私有化的属性可以直接通过dict访问
print(dog1._Dog__name) # 大黄 ,私有化的属性通过_类名__属性名可以访问
3 对象属性的getter和setter
3.1getter和setter
如果希望在获取对象属性之前要做点儿别的事情,就给这个属性添加getter;
如果希望在给对象属性赋值之前做点儿别的事情,就给这个属性添加setter.
3.2给对象属性添加getter
1.属性命名的时候,属性名前加一个_
2.声明一个函数,函数的名字是属性名(不要下划线),不需要参数,有返回值;并且函数前使用@property修饰;这个函数的返回值就是获取属性的结果。
@property
def age(self):
return 年龄相关值
3.当需要获取属性的时候,通过对象.不带下划线的属性。例如:对象.age
3.3给对象属性添加setter
想要给对象属性添加setter,必须先给它添加getter
1.属性命名的时候,属性名前加_;例如self._age=0
2.声明一个函数,函数的名字是属性名(不要下划线),需要一个额外的参数,不用返回值;
并且函数前使用@getter名.setter修饰
@age.setter
def age(self,value)
self._age=value
3.当需要给属性赋值的时候,通过对象.不带下划线的属性来赋值;例如:对象.age=100
class Person:
def __init__(self,name='小红'):
self.name=name
self._age=0
self.sex='男'
# 这儿的age函数就是属性_age的getter方法
@property
def age(self):
if self._age<1:
return '婴儿'
elif self._age<18:
return '未成年'
elif self._age<50:
return '中年'
else:
return '老年'
# 这儿的age函数就是属性_age的setter
@age.setter
def age(self,value):
if not isinstance(value,int):
print('年龄必须是整数')
raise ValueError
if not 0<=value<=100:
print('年龄超出范围')
raise ValueError
self._age=value
补充:打印自己声明的类的对象的时候,默认打印的是:<模块名.类名 object at 对象地址>;如果不希望以默认的方式去打印对象,可以实现__repr__魔法方法。打印对象的时候就会打印这个方法的返回值;这个函数的返回值必须是字符串。
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def __repr__(self):
# return 'name:%s,age:%d'%(self.name,self.age)
return str(self.__dict__)[1:-1]
# 对字典转换来的字符串切片去掉两边的大括号
stu1=Student('小明',29)
stu2=Student('小花',18)
print(stu1,stu2)
4 类的继承
4.1继承
python中的类支持继承,并且支持多继承
python中默认情况是继承自object(object是python中所有类的基类)
1.什么是继承
一个类可以继承另外一个类,继承者我们叫子类,被继承者叫父类。继承就是让子类直接拥有父类中的内容。
2.可以继承哪些内容
所有的属性和方法都可以继承
注意:__slots__魔法对应的值不会被继承
5 添加方法
子类除了拥有从父类继承下来的属性和方法,还拥有属于自己的属性和方法。
5.1在子类中添加方法
5.1.1添加一个新的方法
直接在子类中声明其他的方法;
添加后子类可以拥有自己的方法也可以调用父类的方法,但是父类不能调用子类的方法.
5.1.2重写父类的方法 : 重新实现父类的方法
1.完全重写 - 覆盖父类的功能 - 直接在子类中重新实现父类的方法;
2.部分重写 - 保留父类的功能,添加新的功能 - 在子类中实现父类方法的时候通过super()去调用父类的方法,再添加新的功能。
注意:
1.可以子类的方法中通过super()去调用父类的方法
super(类, 对象) - 获取对象中父类的部分(要求对象是这个指定的类的对象)
2.静态方法中不能使用super()
5.2类中方法的调用过程
1.通过对象或者类调用方法的时候,先看当前类中是否声明过这个方法,如果声明过就直接调用当前类对应的方法;
2.如果当前类中没有声明过,会去找父类中有没有声明过这个方法,声明过就调用父类的方法;
3.如果父类中也没有声明过,就去找父类的父类...以此类推,直到object中也没有声明过,程序才会崩溃。
# 完全重写,函数名与父类 的一样,都是声明在相同的
#对象方法/静态方法/类方法中
@ staticmethod
def info(cls):
print('我是学生')
# 保留父类的功能
def show_message(self):
super().show_message()
print('我去上学')
super().fun1()
6 添加属性
6.1添加类的字段
直接在子类中添加新的字段
6.2添加对象属性
类的对象属性是通过继承父类的init方法继承下来的。
如果想要在保留父类继承下来的对象属性的前提下,添加新的对象属性,需要在子类的init方法中,通过super()去调用父类的init方法。
练习:1.声明一个动物类,有属性:年龄,颜色,类型。
2.要求创建动物对象的时候类型和颜色必须赋值,年龄可以赋值也可以不赋值。
3.声明一个猫类,有属性:年龄,颜色,类型, 爱好
4.要求创建猫对象的时候,颜色必须赋值,年龄和爱好可以赋值也可以不赋值,类型不能赋值
class Animal: # 创建动物类
def __init__(self,color,type,age=1):
self.color=color
self.type=type
self.age = age
class Cat(Animal): # 创建猫类
def __init__(self,color,age=5,hobby='鱼'):
super().__init__(color,'猫科',age)
self.hobby=hobby
cat1=Cat('白色') # 创建对象
print(cat1.type,cat1.hobby,cat1.age,cat1.color) # 猫科 鱼 5 白色
7init、call、new及del方法的使用
init主要用于创建对象时进行初始化操作,会自动调用
new方法是类准备在将自身实例化时调用,是一个静态方法,new(cls, *args, *kwargs):cls是当前正在实例化的类,args, **kwargs是实例化传入的参数,new方法在实例化开始之后,init方法调用之前被调用。
call()方法能够让类的实例对象,像函数一样被调用;
del()是类的析构函数,是python垃圾回收机制的实际应用,当类的所有引用都被删除后,该类就会被系统从内存中删除,注意是所有的引用都被删除,而不是每一次删除;
class Student:
def __init__(self,name ,age):
self.name = name
self.age = age
print('创建对象成功')
def __del__(self):
print('对象已经被删除')
def __call__(self, na):
print('调用call方法','姓名:%s'% na)
def __new__(cls, *args, **kwargs):
print('new方法')
return super(Student,cls).__new__(cls)
stu = Student('鲁班', 18)('李白')
输出依次为:new方法;创建对象成功;调用call方法 姓名:李白;对象已经被删除
class Student:
def __init__(self,name ,age):
self.name = name
self.age = age
print('创建对象成功')
def __del__(self):
print('对象已经被删除')
def __call__(self, *args, **kwargs):
print('调用call方法')
def __new__(cls, *args, **kwargs):
print('new方法')
return super(Student,cls).__new__(cls)
stu = Student('鲁班', 18)
print(stu.name)
输出依次为:new方法;创建对象成功;鲁班;对象已经被删除
可以从上面看出当不在对象后面加()并传参时,call函数不被调用,只有传参了才会被调用;new方法最先被调用;del方法一直都是在对象不被使用后才执行。
当在对象后面加()并传参时,stu = Student('鲁班', 18)('李白'),类中的魔法方法它都会执行,执行完后,它就不在是一个对象,而是一个函数;创建对象不管成功与否,都会执行new方法和del方法,即使错误的使用对象(使用没有该属性的属性)造成程序崩溃,依然会在最后执行del方法。
8单下划线和双下划线属性的区别
"单下划线" 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;"双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;以双下划线开头的(__foo)代表类的私有成员;
class Foo:
def __init__(self):
pass
def public_method(self):
print ('This is public method')
def __fullprivate_method(self):
print ('This is double underscore leading method')
def _halfprivate_method(self):
print ('This is one underscore leading method')
f = Foo()
f.public_method() # ok
f._halfprivate_method() # ok
f.__fullprivate_method() # error
f._Foo__fullprivate_method() #ok
从上面的例子可以看出,f._halfprivate_method()可以直接访问;不过根据python的约定,应该将其视作private,而尽量不要在外部使用它们。f.__fullprivate_method()不能对其进行访问,__fullprivate_method()是私有方法;