面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用。
类是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)。
对象,根据模板创建的实例(即对象),实例用于调用被包装在类中的函数。类是对象的定义 ,而实例是“ 真正的实物”,它存放了类中所定义的对象的具体信息。
面向对象三大特性:封装、继承和多态。
1、类的定义:
1、使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
class ClassName: '类的帮助信息' #类文档字符串 class_suite #类体 #类的帮助信息可以通过ClassName.__doc__查看。 #class_suite 由类成员,方法,数据属性组成。
#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print "Total Employee %d" % Employee.empCount def displayEmployee(self): print "Name : ", self.name, ", Salary: ", self.salary #empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。 #第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法 #self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
2、self代表类的实例,而非类。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt() ''' 以上实例执行结果为: <__main__.Test instance at 0x10d066878> __main__.Test 从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.__class__ 则指向类。 ''' #self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的: class Test: def prt(runoob): print(runoob) print(runoob.__class__) t = Test() t.prt() ''' 以上实例执行结果为: <__main__.Test instance at 0x10d066878> __main__.Test '''
''' 创建实例对象 实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。 以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接收参数。''' #"创建 Employee 类的第一个对象" emp1 = Employee("Zara", 2000) #"创建 Employee 类的第二个对象" emp2 = Employee("Manni", 5000) ''' 访问属性 您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:''' emp1.displayEmployee() emp2.displayEmployee() print "Total Employee %d" % Employee.empCount ''' 可以使用以下函数的方式来访问属性: getattr(obj, name[, default]) : 访问对象的属性。 hasattr(obj,name) : 检查是否存在一个属性。 setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。 delattr(obj, name) : 删除属性。'''
2、类的成员
1、字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同。普通字段属于对象,静态字段属于类。
#coding=utf-8 class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print(obj.name) # 直接访问静态字段 print(Province.country) ''' 由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其静态字段在内存中只保存一份,普通字段在每个对象中都要保存一份。 应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段。 '''
2、类的方法包括实例方法、类方法、静态方法。
class Foo(object): def test(self)://定义了实例方法 print("object") @classmethod def test2(clss)://定义了类方法 print("class") @staticmethod def test3()://定义了静态方法 print("static")
#coding=utf-8 class Apple: def fun1(self): return 'normal' @staticmethod def fun2(): return 'staticmethod' @classmethod def fun3(cls): return 'classmethod' print Apple.fun1 print Apple.fun2 print Apple.fun3 print "-"*80 apple = Apple() print apple.fun1 print apple.fun2 print apple.fun3 print "-"*80 apple1 = Apple() print apple1.fun1 print apple1.fun2 print apple1.fun3 ''' 输出结果: 1、<unbound method Apple.fun1> 2、<function fun2 at 0x00000000022FC4A8> 3、<bound method classobj.fun3 of <class __main__.Apple at 0x0000000001E7C768>> 4、---------------------------------------------------------------------------- 5、<bound method Apple.fun1 of <__main__.Apple instance at 0x00000000022FAE08>> 6、<function fun2 at 0x00000000022FC4A8> 7、<bound method classobj.fun3 of <class __main__.Apple at 0x0000000001E7C768>> 8、---------------------------------------------------------------------------- 9、 <bound method Apple.fun1 of <__main__.Apple instance at 0x00000000022FAE48>> 10、<function fun2 at 0x00000000022FC4A8> 11、<bound method classobj.fun3 of <class __main__.Apple at 0x0000000001E7C768>> --------------------- ------------------------------------------------------ ''' ''' 解析: 1、普通方法传入的第一个参数必须是self(当然也可以不用self,官方要求尽量用self),self是指实例对象本身; 2、静态方法无需传参; 3、类方法传入的第一个参数必须是class,是指类本身。 对比结果1,5,9行: fun1通过class调用时,它是未绑定的方法,而实例化apple和apple1之后,它属于绑定的方法,且实例化后的apple和apple1内存地址不同,因为它们属于不同的实例对象。 对比结果2,6,10行: 静态方法fun2通过class调用或者通过实例化后的对象调用,是没有任何区别的,全部都是指向同一块内存地址。可以简单的理解成静态方法与类或者实例没有任何关系,一旦调用后,它的内存地址即确定。 对比结果3,7,11行: 类方法fun3通过class调用或者通过实例化后的对象调用,是没有任何区别的,全部都是指向同一块内存地址。为什么?因为实例化对象apple和apple1调用类方法fun3传入的第一个参数是类本身Apple,也就是说apple.fun3 = apple1.fun3 = Apple.fun3。 区别总结: 1、静态方法装饰器下定义的方法属于函数(function); 2、类方法装饰器下定义的方法属于方法(method); 3、静态方法无需传入任何参数; 4、类方法传入的第一个参数必须是class本身cls; 5、静态方法与类方法一旦被调用,内存地址即确定。通过类调用和通过实例化对象调用的结果完全一样。 6、三种方法从不同层次上来对方法进行了描述:实例方法针对的是实例,类方法针对的是类,他们都可以继承和重新定义,而静态方法则不能继承,可以认为是全局函数 '''
3、如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。
#coding=utf-8 # ############### 定义 ############### class Foo: def func(self): pass # 定义属性 @property def prop(self): pass # ############### 调用 ############### foo_obj = Foo() foo_obj.func() foo_obj.prop #调用属性 ''' 由属性的定义和调用要注意一下几点: 定义时,在普通方法的基础上添加 @property 装饰器; 定义时,属性仅有一个self参数 调用时,无需括号 方法:foo_obj.func() 属性:foo_obj.prop 注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象 属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能 '''
属性的两种定义方式
属性的定义有两种方式:
装饰器 即:在方法上应用装饰器
静态字段 即:在类中定义值为property对象的静态字段
''' 装饰器方式 装饰器方式:在类的普通方法上应用@property装饰器 我们知道Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 ) 经典类,具有一种@property装饰器(如上一步实例) ''' # ############### 定义 ############### class Goods: @property def price(self): return "wupeiqi" # ############### 调用 ############### obj = Goods() result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 #新式类,具有三种@property装饰器 # ############### 定义 ############### class Goods(object): @property def price(self): print '@property' @price.setter def price(self, value): print '@price.setter' @price.deleter def price(self): print '@price.deleter' # ############### 调用 ############### obj = Goods() obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price= 123 # 自动执行 @price.setter修饰的price方法,并将123赋值给方法的参数 del obj.price # 自动执行 @price.deleter 修饰的 price 方法 ''' 注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法 由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除 '''
#静态字段方式,创建值为property对象的静态字段 #当使用静态字段的方式创建属性时,经典类和新式类无区别 class Foo: def get_bar(self): return 'good' BAR = property(get_bar) obj = Foo() reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值 print result ''' property的构造方法中有个四个参数 第一个参数是方法名,调用 对象.属性 时自动触发执行方法 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息 '''
3、新式类和旧式类
# -*- coding: utf-8 -*- # 经典类或者旧试类 class A: pass a = A() # 新式类 class B(object): pass b = B() # python2不支持 # print(A.__class__) print(a.__class__) print(type(A)) print(type(a)) # python2 # __main__.A # <type 'classobj'> # <type 'instance'> # python3 # <class 'type'> # <class '__main__.A'> # <class 'type'> # <class '__main__.A'> print(B.__class__) print(b.__class__) print(type(B)) print(type(b)) # python2 # <type 'type'> # <class '__main__.B'> # <type 'type'> # <class '__main__.B'> # python3 # <class 'type'> # <class '__main__.B'> # <class 'type'> # <class '__main__.B'> # 旧式类的实现不够好,类是类,实例是实例,类的类型是classobj,实例的类型是instance,两者的联系只在于__class__, # 这和内置对象是不同的,int对象的类型就是int,同时int()返回的也是int类型的对象,内置对象和自定义对象不同就对代码统一实现带来很大困难。 # # 新式类 # # 1. 所有类的类型都是type # 2. 所有类调用的结果都是构造,返回这个类的实例 # 3. 所有类都是object的子类 # 4. 新式类不仅可以用旧类调用父类的方法,也可以用super方法。
4、类的私有成员
1):单下划线_开头:只是告诉别人这是私有属性,外部依然可以访问更改
2):双下划线__开头:外部不可通过实例化对象.属性名来访问或者更改
实际将其转化为了_类名_属性名,只是在内部将变量名修改了,我们仍让可以通过._类名_属性名访问
>>>class Test(): >>> a = 100 >>>Test.__dict__ Out[3]: {'__doc__': None, '__module__': '__main__', 'a': 100} >>>class Test(): >>> __a = 100 >>>Test.__dict__ Out[5]: {'_Test__a': 100, '__doc__': None, '__module__': '__main__'} >>>Test._Test__a Out[6]: 100
私有成员和公有成员的访问限制不同:
1) 静态字段
公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
私有静态字段:仅类内部可以访问;
2) 普通字段
公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
私有普通字段:仅类内部可以访问;
注意:如果想要强制访问私有字段,可以通过【对象._类名__私有字段明】访问(如:obj._C__foo),不建议强制访问私有成员。方法、属性的访问于上述方式相似,即:私有成员只能在类内部使用
5、类成员的修改
1、修改类的对象(实例)属性
#coding=utf-8 class Test(object): a = 100 def __init__(self): pass t1 = Test() t2 = Test() #1.通过实例对象t1修改类属性的值 print("修改前:t1.a = %d, t2.a = %d, Test.a = %d"%(t1.a, t2.a, Test.a)) t1.a += 100 print("修改后:t1.a = %d, t2.a = %d, Test.a = %d"%(t1.a, t2.a, Test.a)) ''' 输出: 修改前:t1.a = 100, t2.a = 100, Test.a = 100 修改后:t1.a = 200, t2.a = 100, Test.a = 100 '''
很明显,我们通过实例对象修改类属性的值,并没有在全局范围奏效,它只是对t1对象的实例属性修改了,实际上并没有对本身定义的类属性和其它的对象的属性造成变动。
2、修改类的属性:
class Test(object): a = 100 def __init__(self): pass t1 = Test() t2 = Test() #1.通过类名修改类属性的值 print("修改前:t1.a = %d, t2.a = %d, Test.a = %d"%(t1.a, t2.a, Test.a)) Test.a += 100 print("修改后:t1.a = %d, t2.a = %d, Test.a = %d"%(t1.a, t2.a, Test.a)) ''' 输出: 修改前:t1.a = 100, t2.a = 100, Test.a = 100 修改后:t1.a = 200, t2.a = 200, Test.a = 200 '''
通过类名修改的类属性,在全局范围奏效,也就是说确实修改了类属性的值,在实例对象中我们看到属性值全部被修改。
6、类的内置特殊成员
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' obj = Province('HeBei',10000) # 自动执行类中的 __init__ 方法 print obj ''' #__init__构造方法,通过类创建对象时,自动触发执行。 输出: <__main__.Province instance at 0x0000000003989108> '''
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' def __del__(self): #当对象在内存中被释放时,自动触发执行。 pass obj = Province('HeBei',10000) print obj ''' #__del__析构方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。 输出: <__main__.Province instance at 0x0000000003989108> '''
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' def __del__(self): pass def __call__(self): print "__call__" obj = Province('HeBei',10000) print obj() #对象后面加括号,触发执行。 ''' #__call__构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() 输出: __call__ None '''
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' def __del__(self): pass def __call__(self): print "__call__" def __str__(self): return("__str__") obj = Province('HeBei',10000) print obj ''' #__str__如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值。 输出: __str__ '''
#用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__。 #a)第一步 class Foo(object): pass obj = Foo() for i in obj: print i # 报错:TypeError: 'Foo' object is not iterable #b) 第二步 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __iter__(self): pass obj = Foo() for i in obj: print i # 报错:TypeError: iter() returned non-iterator of type 'NoneType' #c) 第三步 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __init__(self, sq): self.sq = sq def __iter__(self): return iter(self.sq) obj = Foo([11,22,33,44]) for i in obj: print i #d)For循环语法内部 #!/usr/bin/env python # -*- coding:utf-8 -*- obj = iter([11,22,33,44]) while True: val = obj.next() print val
#coding=utf-8 class Foo(object): def __getitem__(self, key): print('__getitem__', key) def __setitem__(self, key, value): # print('__setitem__', key, value) self.key = key self.value = value print(key,value) return self.key def __delitem__(self, key): print('__delitem__', key) obj =Foo() result = obj['k1'] # 自动触发执行 __getitem__ obj['k3']='xxxxxx' # 自动触发执行 __setitem__ del obj['k1'] # 自动触发执行 __delitem__
#该三个方法用于分片操作,如:列表。 #!/usr/bin/env python # -*- coding:utf-8 -*- class Foo(object): def __getslice__(self, i, j): print '__getslice__',i,j def __setslice__(self, i, j, sequence): print '__setslice__',i,j def __delslice__(self, i, j): print '__delslice__',i,j obj = Foo() obj[-1:1] # 自动触发执行 __getslice__ obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__ del obj[0:2] # 自动触发执行 __delslice__
''' __new__()在Python :类里面的构造方法init()负责将类的实例化,而在init()调用之前,new()决定是否要使用该init()方法, 因为new()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。 如果将类比喻为工厂,那么init()方法则是该工厂的生产工人,init()方法接受的初始化参 数则是生产所需原料,init()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 new()则是生产部经理,new()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。 new()方法的特性: new()方法是在类准备将自身实例化时调用。 new()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。 ''' ''' 通常来说,新式类开始实例化时,new()方法会返回cls(cls指代当前类)的实例,然后该类的 init()方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入new ()方法中接收的位置参数和命名参数。 注意:如果new()没有返回cls(即当前类)的实例,那么当前类的init()方法是不会被调用 的。 如果new()返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方 法。 ''' class Foo(object): def __init__(self, *args, **kwargs): pass def __new__(cls, *args, **kwargs): return object.__new__(Stranger, *args, **kwargs) #重写Foo类的new方法,返回Stranger类的实例 class Stranger(object): pass foo = Foo() print(type(foo)) #结果显示foo其实是Stranger类的实例 <class '__main__.Stranger'> #链接:https://blog.csdn.net/Four_Infinite/article/details/52798919 #__init__与__new__的先后执行顺序 class Test(object): def __init__(self,name): self.name = name print "hey" def __new__(cls, *args, **kwargs): print "hello" return object.__new__(cls, *args, **kwargs) f = Test("study") ''' 输出: hello hey '''
#阅读以下代码: class Foo(object): def __init__(self): pass obj = Foo() # obj是通过Foo类实例化的对象 ''' 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。 如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。 print type(obj) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建 所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。 ''' #那么,创建类就可以有两种方式: #a) 普通方式 class Foo(object): def func(self): print 'hello wupeiqi' #b) 特殊方式(type类的构造函数) def func(self): print 'hello wupeiqi' Foo = type('Foo',(object,), {'func': func}) #type第一个参数:类名 #type第二个参数:当前类的基类 #type第三个参数:类的成员 ''' ==》 类 是由 type 类实例化产生 那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象? 答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。 '''
''' __new__ is the method called before __init__ It's the function that actually creates the object and returns it. __init__ only initialize the object passed as a parameter. We rarely use __new__, except when we want to control how the object is created. For a metaclass, the object created is a class. And since we want to customize it, we need to override __new__. We can also do something by overriding __init__ to get customized initialization process as well. Advanced usage involves override __call__, but we won't talk about this here. ''' #链接:https://www.jianshu.com/p/224ffcb8e73e #链接:https://www.jianshu.com/p/14b8ebf93b73
class Test(type): pass class Name(object): __metaclass__ = Test f = Name() print type(f) print type(Name) print type(Test) ''' 输出: <class '__main__.Name'> <class '__main__.Test'> <type 'type'> ''' class Test(type): def __init__(self,*args,**kwargs): super(Test,self).__init__(*args,**kwargs) def test(self,name): return name class Name(object): __metaclass__ = Test def __init__(self,what): self.what = what def sayhi(self): return self.what f = Name("cat") print type(f) print type(Name) print type(Test) ''' 输出: <class '__main__.Name'> <class '__main__.Test'> <type 'type'> '''
class Fo(): pass class FFo(object): pass print type(Fo) print type(type(Fo)) print type(FFo) ''' 输出: <type 'classobj'> <type 'type'> <type 'type'> '''
#__doc__表示类的描述信息
#__module__表示当前操作的对象在那个模块,类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
#__class__表示当前操作的对象的类是什么
#__name__表示类名
#__bases__类的所有父类构成元素(包含了一个由所有父类组成的元组)
#__dict__表示类或对象中的所有成员(包含一个字典,由类的数据属性组成)。
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' print Province.__doc__ print Province.__module__ #print Province.__class__ #AttributeError: class Province has no attribute '__class__' print Province.__name__ print Province.__bases__ print Province.__dict__ ''' #__doc__表示类的描述信息 #__module__表示当前操作的对象在那个模块,类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod) #__class__表示当前操作的对象的类是什么 #__name__表示类名 #__bases__类的所有父类构成元素(包含了一个由所有父类组成的元组) #__dict__表示类的属性(包含一个字典,由类的数据属性组成) 输出: Genius is thing but labor and diligence! __main__ Province () {'country': 'China', '__module__': '__main__', 'func': <function func at 0x000000000329C358>, '__doc__': 'Genius is thing but labor and diligence!', '__init__': <function __init__ at 0x000000000329C2E8>} '''
#coding=utf-8 class Province: """Genius is thing but labor and diligence!""" country = 'China' def __init__(self, name, count): self.name = name self.count = count def func(self, *args, **kwargs): city="New" print 'func' obj = Province('HeBei',10000) print obj.__doc__ print obj.__module__ print obj.__class__ #print obj.__name__ #AttributeError: Province instance has no attribute '__name__' #print obj.__bases__ #AttributeError: Province instance has no attribute '__bases__' print obj.__dict__ ''' #__doc__表示类的描述信息 #__module__表示当前操作的对象在那个模块,类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod) #__class__表示当前操作的对象的类是什么 #__name__表示类名 #__bases__类的所有父类构成元素(包含了一个由所有父类组成的元组) #__dict__表示类的属性(包含一个字典,由类的数据属性组成) 输出: Genius is thing but labor and diligence! __main__ __main__.Province {'count': 10000, 'name': 'HeBei'} '''
7、类的继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
''' 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。 继承概念的实现方式主要有2类:实现继承、接口继承。 实现继承是指使用基类的属性和方法而无需额外编码的能力。 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。 在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。 OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。 ''' #coding=utf-8 class Person(object): # 定义一个父类 def talk(self): # 父类中的方法 print("person is talking....") class Chinese(Person): # 定义一个子类, 继承Person类 def walk(self): # 在子类中定义其自身的方法 print('is walking...') c = Chinese() c.talk() # 调用继承的Person类的方法 c.walk() # 调用本身的方法 ''' # 输出 person is talking.... is walking... '''
""" 如果我们要给实例 c 传参,我们就要使用到构造函数,那么构造函数该如何继承,同时子类中又如何定义自己的属性? 继承类的构造方法: 1.经典类的写法: 父类名称.__init__(self,参数1,参数2,...) 2. 新式类的写法:super(子类,self).__init__(参数1,参数2,....) """ #coding=utf-8 class Person(object): def __init__(self, name, age): self.name = name self.age = age self.weight = 'weight' def talk(self): print("person is talking....") class Chinese(Person): def __init__(self, name, age, language): # 先继承,在重构 Person.__init__(self, name, age) #继承父类的构造方法,也可以写成:super(Chinese,self).__init__(name,age) self.language = language # 定义类的本身属性 def walk(self): print('is walking...') class American(Person): pass c = Chinese('bigberg', 22, 'Chinese')
class Person(object): def __init__(self, name, age): self.name = name self.age = age self.weight = 'weight' def talk(self): print("person is talking....") class Chinese(Person): def __init__(self, name, age, language): Person.__init__(self, name, age) self.language = language print(self.name, self.age, self.weight, self.language) def talk(self): # 子类 重构方法 print('%s is speaking chinese' % self.name) def walk(self): print('is walking...') c = Chinese('bigberg', 22, 'Chinese') c.talk() ''' # 输出 bigberg 22 weight Chinese bigberg is speaking chinese '''
8、类的多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
""" 编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话,对不同类的对象发出相同的消息将会有不同的行为。 多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。 """ #一个抽象类具有多个子类,就是多种形态。多态依赖于继承,没有继承就没有多态。 class Animal(object): def __init__(self, name, age): self.name = name self.age = age pass class Dog(Animal): def __init__(self, name, age, breed): super(Dog, self).__init__(name, age) self.breed = breed pass class Cat(Animal): def __init__(self, name, age, breed): super(Cat, self).__init__(name, age) self.breed = breed pass #我们定义了一个 Animal类,又定义了其两个子类 :Dog 和 Cat。这两个子类就表示了Animal这个类的多种形态。
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
9、对象销毁(垃圾回收)
要保持追踪内存中的对象,Python使用了引用计数这一简单技术。也就是说Python内部记录着所有使用中的对象 各有多少引用。一个内部跟踪变量,称为引用计数器。每个对象各有多少个引用,简称引用计数。当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,也就是说,这个对象的引用计数变为0时,它被垃圾回收。(并不是100%这样)
链接:https://www.cnblogs.com/bigberg/p/7591463.html