16-面向对象之语法(1)
1. 举例:
class People(self,name,age):
def __init__(self,name,age):
self.name = name
self.age = age
def run(self):
print("I can run")
p1 = People("Tom", 22) //实例化,得到一个instance
# self的含义代表“当前这个对象,这个instance”,与当前这个class区分开
2. _init_() 和 _new_() 的功能与区分:
- _new_() 是创建instance(叫对象或者实例都可以)
- _init_() 是初始化上面创建好的instance
我个人觉得:init加上new,近似等价于java里面的构造方法,当实例化class得到了一个instance时,系统自动调用new和init
- 定义class的fun时需要传入self参数,但是调用时系统自动传入self,这个参数无须用户处理:定义时要形参写self, 但是真正传实参时忽略它。
- _str_() 的激发时机:当系统要print(某对象)时,会自动激发此函数
- 以上三个魔术方法,都可以用户自定义或者不管(系统默认有)
3. 属性私有化:
__name,__run() 属性或者方法名前面两个下划线,即变成只有class内部能够访问的私有属性
(不是完全没办法class外部访问,只是建议私有属性和方法不要在class外部直接访问)
4. 继承:
class Student(People) #就是这样简单地继承了People
实现的效果:
- Student这个class默认拥有People所有的公开属性及公开方法;
- 私有属性和方法不能被继承;
- 子类继承父类的公有属性的本质:子类默认继承了父类的_init_() ;所以如果子类重写了_init_(),子类就去调用自己的_init_(),就不一定会继承父类的所有公共属性。这一点切记!看透继承公共属性的本质!
- 继承父类的公共方法的本质:也是默认继承了父类所有的公共方法。如果子类重写了这些方法,也是直接先去调用自己的方法。
- 故而,一切继承行为的本质:有没有默认调用父类的某个方法,如果子类重写了,那就去调用自己的方法,那就不一定能得到父类所有的东西。
默认所有类都有一个基类:object(都是默认继承自object),默认继承objext的所有属性和方法。
重写:
- 子类与父类的方法名相同,子类即重写了此方法,优先去调用自己的方法。
- 子类重写了父类的方法之后,依旧希望调用父类的方法,有以下方案:
- super().父类方法名(实参)
- super(子类名,self).父类方法名(实参)
- 父类名.父类方法名(self,实参)
class Animal(object):
def __init__(self,name):
self.name = name
print("Animal init\n")
class Cat(Animal):
def __init__(self):
print("Cat init\n") #再执行自己的逻辑
class Dog(Animal):
def __init__(self,color): #实例化对象需要传入color
super().__init__("DogName") #调用父类的__init__(参数要根据父类的init形式写)
print("Dog init\n")
self.color = color
class Pig(Animal):
def __init__(self): #实例化对象需要传入color
super(Pig,self).__init__("PigName") #调用父类的__init__(参数要根据父类的init形式写)
print("Pig init\n")
class Chiken(Animal):
def __init__(self): #实例化对象需要传入color
Animal.__init__(self,"PigName") #调用父类的__init__(参数要根据父类的init形式写)
print("Chiken init\n")
c = Cat()#因为Cat的init重写了,根据重写的init的参数形式来构造实例
#print(c.name) #Cat 虽然继承了Animal,Animal有name,但是此处Cat无法获得name
d = Dog("black")
p = Pig()
c = Chiken()
#本文的__init__()可换成父类的任意公共方法
#而父类的公共属性的继承的本质:一定要记住:是由于继承父类的__init__()而得到的,
#没有继承父类的__init__()就无法继承父类的公共属性
5. 多继承:
- class C(B,A):写在前面的父类优先级高(B比A高),当前这个class本身的优先级最高(C>B>A),这个class的实例优先级最低(C>B>A>The_instance_of_C)。__mro__是当前class的类级别的属性,标识了当前类体系里面优先级的高低。
6.多态:
- 多态本意是:强类型语言中,某变量声明为A类型,使用时“看起来不是”A类型:看起来有多种形态。
- python任何变量皆对象(包括函数都是instance, int 也是instance),所以多态无意义
- python强调“鸭子类型”:只要具备相同的方法,每个对象都认为是“相似”的。
7.类属性与实例属性
-
类属性属于class,它在内存中只有一个副本。可以被所有instance使用, 具体说即使没有instance,这个类属性依旧存在,它与instance无关!(与java的static变量相似)。
-
实例属性:必须用self.attr标识,隶属于一个instance,与这个instance共存亡,与当前的class没有关系。
class Person(object):
name = "person" #公有的
__passwd = 2018 #私有的def init(self,sex):
self.sex = sex #self.sex是实例属性,必须用self标识:与对象共存亡print(Person.name,'\n')
p1 = Person("female")
print(p1.sex) #实例属性:生命周期与instance相关
print(p1.name)
print(Person.name,'\n') #类属性:生命周期与class相关p2 = Person("male")
print(p2.sex)
print(p2.name)
print(Person.name,'\n')
8.类的静态方法与类方法:
1. 三个重要结论:
- 类属性和类方法,都是属于class,所以能被子类继承。继承这个操作是class层次的行为
- 类属性和类方法可以通过类的instance或者类名来调用
- 类属性的修改和删除只能通过类名
*类方法必定有个形参(cls) {类比普通方法必定有个self}
*类方法使用@classmethod 标识
2. 静态方法(类方法中的特例):
-
@staticmethod标识,形参可以为空,但一旦传入多少形参,就要传入多少实参
class A(object):
name = "hao" #类属性def test_1(self): #普通方法
print("A的普通方法 test_1")@classmethod #标识类方法的关键字,{类比java的注解}
def test_2(cls): #cls,标识当前这个class
print("A的类方法 test_2")@staticmethod #标识类的静态方法
def test_3(): #形参可以为空,但是若写了就必须传入全部参数,因为这里没有self或者cls
print("A的类方法静态方法 test_3")print(A.name)
A.test_1() #普通方法不能通过类名调用
A.test_2()
A.test_3()
print('\n')a = A()
print(a.name)
a.test_1()
a.test_2()
a.test_3()
3. 普通实例方法:
- 必定有个形参self,实参却不用自己传入self(系统自动传入)
- self引用的即可以是类属性,也可以是实例属性 (具体情况具体分析)
- 而对于类方法,cls引用的一定是类属性和类方法;对于静态方法,需要用类名来引用类属性和类方法(也只能访问类属性和类方法)。
4. 再次回顾子类重写父类的方法,对于公共属性和公共方法的影响:
- 如果没有重写行为,默认继承了父类所有的公共属性和公共方法。
- 如果子类对_init_()执行重写,那么创建的对象优先执行子类重写后的_init_(), 所以有些父类的公共实例属性可能不能被继承到子类,但类属性不受影响,依旧可以继承下来,因为它不在__init__里面
- 如果子类对哪个公共方法进行重写,那么子类的对象就首先只执行重写后的那个方法
9. _del_():类比java的析构方法
某对象最后一次被引用完毕,自动执行_del_(),系统回收这个对象的内存空间时