Python里的类和和日常生活中说的类,如人类,鸟类,基本含义一致。即把具有相同属性和行为的一类事物定义为一个类。类是封装对象属性和行为的载体。这体现了类的封装性。同样作为鸟类,有的鸟两米高有的鸟却只有蜜蜂一般大,这体现的是类的多态性。大鸭子生的小鸭子会游泳,会呱呱叫,这可以理解是类的继承性。
1.属性
(1) 实例属性
如人这一类事物,应该有高矮、胖瘦、贫富等属性。具体到某一个人,他的高矮、胖瘦等属性就属于他自己的实例属性。
class Person():
def __init__(self, height, weight, wealth):
self.height = height
self.weight = weight
self.wealth = wealth
if __name__ == "__main__":
p = Person(1.75, 60, 100)
(2) 类属性
但是,有些属性是类共有的,比如人类都有一颗爱心。不需要每个实例单独去实现。这样的属性叫类属性。
class Person():
love = True
def __init__(self, height, weight, wealth):
self.height = height
self.weight = weight
self.wealth = wealth
(3) 私有属性
还有一些属性可能是隐私,不想被别人知道。这样的属性称为私有属性。比如长得矮的不想让别人知道身高,就需要设置访问权限。单下划线_height只允许类和子类访问,双下划线只允许本类访问,类的实例也不行。
class Person():
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
(4)属性访问
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
if __name__ == "__main__":
p = Person(1.55, 70, 200)
# 普通的实例属性和类属性都可以通过实例.属性方式访问
print('我家有{}万财产'.format(p.wealth), '我{}爱心'.format('有' if p.love else '没有'))
# 单下划线并没有真正实现属性保护,只是防止了直接通过p.height获取
print(p._height)
# 双下划线表示隐私性很强了,只有下面的方式可以访问,但仍然可以访问
print(p._Person__weight)
# 输出:我家有200万财产 我有爱心
1.55
70
(5)添加和修改属性
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
if __name__ == "__main__":
p = Person(1.55, 70, 200)
p2 = Person(1.7, 60, 10)
# 修改实例属性值p.wealth
p.wealth = 1000
print('我现在的财富变成了{}万'.format(p.wealth))
# 这个操作相当于是新增属性,并不会修改类的对应属性
p.love = False
print('一个败类并不会影响全人类的特性,我是{}爱心的'.format('有' if p2.love else '没有'))
# 新增实例属性
p2.__dict__['system'] = '资本主义'
print('曾经我认为{}制度是最优的'.format(p2.system))
# 修改实例属性
p2.__dict__['system'] = '共产主义'
print('现在我坚定地认为{}制度才是是最优的'.format(p2.system))
# 输出:我现在的财富变成了1000万
一个败类并不会影响全人类的特性,我是有爱心的
曾经我认为资本主义制度是最优的
现在我坚定地认为共产主义制度才是是最优的
(6)动态属性
前面讲了,你的身高可能是私有属性。但你对外需要报一个身高,那么我们可以使用动态属性。即把方法的调用变成和属性调用一样。
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
@property
def height(self):
return self._height + 0.1
if __name__ == "__main__":
p = Person(1.55, 70, 200)
print('我对外公布的身高是{}'.format(p.height))
# 输出:我对外公布的身高是1.65
2.方法
(1)实例方法
方法就是前面提到的行为。比如,不管贫穷还是富贵,我们都要吃饭吧。实例方法的特点就是参数里有self,即实例本身。因此,你可以在方法里使用这个实例。
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
@property
def height(self):
return self._height + 0.1
def eat(self):
if self.wealth > 100:
self.wealth -= 1
print('我花1万吃了一顿饭')
else:
self.wealth -= 0.01
print('我花1百吃了一顿饭')
if __name__ == "__main__":
p = Person(1.55, 70, 200)
p.eat()
print('我现在的余额是{}'.format(p.wealth))
# 输出:我花1万吃了一顿饭
我现在的余额是199
(2)静态方法
顾名思义,就是方法是静态的,固定的,与实例无关的。实际上这个特点和在类外定义的函数一致。之所以还要在类里定义静态方法,主要还是从业务需求出发。比如,有的方法在其他地方用不到,只在某个类里使用。为了使代码有较强封装性,就在这个类里写静态方法。
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
@staticmethod
def think():
print('仰望星空,思考人生')
if __name__ == "__main__":
p = Person(1.55, 70, 200)
p.think()
3.继承
(1)继承和方法重写
我们和父辈长得像,这是继承他们的基因。同样,中华民族的优秀品质和文化也被一代一代继承至今。在程序开发中,类的继承的意义就是在复制的基础上新增和修改。尽可能地避免重复代码。
class Person:
love = True
def __init__(self, height, weight, wealth):
self._height = height
self.__weight = weight
self.wealth = wealth
@staticmethod
def think():
print('仰望星空,思考人生')
class Student(Person):
def __init__(self, height, weight, wealth, school):
super().__init__(height, weight, wealth)
self.school = school
# 这里是方法重写,学生和普通人思考地问题不一样
# 如果没有定义,学生类仍然可调用think方法,因为从人类那里继承来的
@staticmethod
def think():
print('今天学了什么内容,晚上找莉莉散个步')
# 新增了属于学生类的方法
@staticmethod
def do_homework():
print('做一张试卷')
if __name__ == "__main__":
s = Student(1.55, 70, 200, '河海大学')
s.think()
s.do_homework()
(2)调用父类一般方法
上面的例子中,我们已经调用了父类的构造方法。实际上,父类的其他方法都可以在子类中调用。
class Father:
love = True
def __init__(self, name='xx'):
self.name = name
@staticmethod
def give():
return 100
class Son(Father):
def __init__(self, wealth):
self.wealth = wealth
super().__init__()
def buy_house(self):
# 调用父类的方法
get = super().give()
print('爸爸给了我{}万'.format(get))
self.wealth += get
house_price = 120
self.wealth -= house_price
if __name__ == "__main__":
s = Son(20)
s.buy_house()
print('买完房后,我的余额{}'.format(s.wealth))
# 输出:爸爸给了我100万
买完房后,我的余额0
(3)强制要求子类实现某方法
为了保证功能,父类有时候需要要求子类在继承时必须实现某些方法。方法一是调用父类方法时抛异常。
class Father:
def earn_money(self):
raise NotImplemented
class Son(Father):
def say(self):
print('baba')
s = Son()
s.say()
# 子类没有实现这个方法,调用会报错
s.earn_money()
另一个方法是定义抽象基类和抽象方法,这样子类如果没有覆盖该抽象方法,则子类实例化时就会报错。
class Father(metaclass=abc.ABCMeta):
@abc.abstractmethod
def earn_money(self):
pass
class Son(Father):
def say(self):
print('baba')
# 实例化就会报错
s = Son()
4.其他知识
(1)new和init的区别
class User:
# 对类进行操作, 执行顺序先于__init__
def __new__(cls, *args, **kwargs):
print("new")
if kwargs['age'] < 18:
return
# 此处如不返回类,则不会调用__init__
return super().__new__(cls)
# 对实例进行操作
def __init__(self, name):
print("init")
self.name = name
if __name__ == "__main__":
user = User(name="bob", age=17)
(2)isinstance和type的区别
class A:
pass
class B(A):
pass
b = B()
# 只要有继承关系,不一定是直接继承
print(isinstance(b, B))
print(isinstance(b, A))
print(type(b) is B)
# type只会往上查询一级
print(type(b) is A)
# 输出:True
True
True
False
(3)多继承
class A:
pass
class B:
pass
class C(B, A):
pass
(4) 魔法方法
在类里定义一些魔法方法,就可以使类具备某些特性。
class Company:
def __init__(self, employ_list):
self.employ = employ_list
# 实现这个方法就具备求长度的性质
def __len__(self):
return len(self.employ)
# 实现这个方法就具备可迭代性质
def __getitem__(self, item):
return self.employ[item]
c = Company(['张三', '李四'])
print('公司人数{}'.format(len(c))
for person in c:
print(person)
(5)继承中可能出现的属性值陷阱问题,子类和父类中同名属性会相互影响。
class P(object):
def __init__(self):
self.value = 0
print('父类初始化时,该属性的值:{}, id:{}'.format(self.value, id(self.value)))
def get(self):
# 父类中修改,子类也会受影响
self.value = 10
print('调用父类get,该属性的值:{}'.format(self.value))
print('调用父类get,该属性的id:{}'.format(id(self.value)))
class C(P):
def __init__(self):
# 先调用父类构造函数。父类构造函数中的属性在子类中可以使用
super(C, self).__init__()
print('直接从父类继承来的属性值:{}, id:{}'.format(self.value, id(self.value)))
# 这一句会覆盖父类中同名属性,且父类的的属性也会相应更改
self.value = 100
print('子类初始化后,该属性的值:{}, id:{}'.format(self.value, id(self.value)))
def get(self):
print('调用子类get,该属性的值{}'.format(self.value))
print('调用子类get,该属性的id{}'.format(id(self.value)))
def get_super(self):
return super(C, self).get()
c = C()
c.get_super()
c.get()
# 输出:父类初始化时,该属性的值:0, id:140711492811808
直接从父类继承来的属性值:0, id:140711492811808
子类初始化后,该属性的值:100, id:140711492815008
调用父类get,该属性的值:10
调用父类get,该属性的id:140711492812128
调用子类get,该属性的值10
调用子类get,该属性的id140711492812128