对象是现实生活中具体存在的事物,他可以看得见摸得着。
类是抽象的,它是对一群具有相同特征和行为的事物的统称。
类的组成
类是由3部分组成:
1、类名:类的名称,它的首字母必须大写。
2、属性:用于描述事物的特征。
3、方法:用于描述事物的行为。
定义一个类,示例代码如下:
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....")根据类创建对象
创建对象格式如下:
对象名 = 类名()
创建对象的过程:
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") #创建一个对象 tom = Cat()调用对象的方法
tom.eat() tom.drink()给对象添加属性
添加属性的格式如下:
对象名.新的属性名 = 值
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") #创建一个对象 tom = Cat() #调用tom指向的对象中的 方法 tom.eat() tom.drink() tom.name = "汤姆" tom.age = 40
获取对象的属性
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): print("%s的年龄是:%d"%(tom.name, tom.age)) #创建一个对象 tom = Cat() #调用tom指向的对象中的 方法 tom.eat() tom.drink() #给tom指向的对象添加2个属性 tom.name = "汤姆" tom.age = 40 #获取属性的第1种方式 #print("%s的年龄是:%d"%(tom.name, tom.age)) tom.introduce()注意:多个方法间通过对象的属性来完成数据共享
创建多个对象
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): print("%s的年龄是:%d"%(tom.name, tom.age)) #创建一个对象 tom = Cat() #调用tom指向的对象中的 方法 tom.eat() tom.drink() #给tom指向的对象添加2个属性 tom.name = "汤姆" tom.age = 40 #获取属性的第1种方式 #print("%s的年龄是:%d"%(tom.name, tom.age)) tom.introduce() #创建第二个对象 lanmao = Cat() lanmao.name = "蓝猫" lanmao.age = 10 lanmao.introduce()
self的使用
在方法的定义中,第一个参数永远都是self。self的字面意思是自己,表示的是对象自身。当某个对象调用方法的时候,Python解释器会把这个对象作为第一个参数传给self,开发者只需要传递后面的参数就可以了。
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): #print("%s的年龄是:%d"%(tom.name, tom.age)) print("%s的年龄是:%d"%(self.name, self.age)) #创建一个对象 tom = Cat() #调用tom指向的对象中的 方法 tom.eat() tom.drink() #给tom指向的对象添加2个属性 tom.name = "汤姆" tom.age = 40 #获取属性的第1种方式 #print("%s的年龄是:%d"%(tom.name, tom.age)) tom.introduce()#相当于 tom.introduce(tom) lanmao = Cat() lanmao.name = "蓝猫" lanmao.age = 10 lanmao.introduce()
slef的理解
__init__()方法
当创建类的实例的时候,系统就会自动调用__init__()方法,从而实现对类进行初始化的操作。
class Cat: """定义了一个Cat类""" #初始化对象 def __init__(self, new_name, new_age): self.name = new_name self.age = new_age #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): print("%s的年龄是:%d"%(self.name, self.age)) #创建一个对象 tom = Cat("汤姆", 40) tom.eat() tom.drink() #tom.name = "汤姆" #tom.age = 40 tom.introduce() lanmao = Cat("蓝猫", 10) #lanmao.name = "蓝猫" #lanmao.age = 10 lanmao.introduce()__del__()方法
当删除一个对象来释放类所占用的资源的时候,Python解释器默认会调用另外一个方法,这个方法就是__del__()方法。
class Cat: """定义了一个Cat类""" #初始化对象 def __init__(self, new_name, new_age): self.name = new_name self.age = new_age def __del__(self): print("------def-----") #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): print("%s的年龄是:%d"%(self.name, self.age)) #创建一个对象 tom = Cat("汤姆", 40) tom.eat() tom.drink() #tom.name = "汤姆" #tom.age = 40 tom.introduce() lanmao = Cat("蓝猫", 10) #lanmao.name = "蓝猫" #lanmao.age = 10 lanmao.introduce() del lanmao print("-------1111-----")
class Dog: def __del__(self): print("-----英雄over------") dog1 = Dog() dog2 = dog1 del dog1#不会调用 __del__方法,因为这个对象 还有其他的变量指向它,即 引用计算不是0 del dog2#此时会调用__del__方法,因为没有变量指向它了 print("====================") #如果在程序结束时,有些对象还存在,那么python解释器会自动调用它们的__del__方法来完成清理工作
__str__()方法
__str__方法和__init__方法类似,都是一些特殊方法,所以前后都有双下划线,它用来返回对象的字符串表达式。
class Cat: """定义了一个Cat类""" #初始化对象 def __init__(self, new_name, new_age): self.name = new_name self.age = new_age def __str__(self): return "%s的年龄是:%d"%(self.name, self.age) #方法 def eat(self): print("猫在吃鱼....") def drink(self): print("猫正在喝kele.....") def introduce(self): print("%s的年龄是:%d"%(self.name, self.age)) #创建一个对象 tom = Cat("汤姆", 40) lanmao = Cat("蓝猫", 10) print(tom) print(lanmao)注意:在我们编写一个新的Python类的时候,总是在最开始位置写一个初始化方法__init__,以便初始化对象,然后会写一个__str__方法,方面我们调试程序。
封装(隐藏数据和保护属性)
通常把隐藏属性、方法与方法实现细节的过程称为封装。
如果有一个对象,当需要对其进行修改属性时,有2中方法
(1)、对象名.属性名 = 数据 --->直接修改
(2)、对象名.方法名() --->间接修改
为了更好的保护属性安全,即不能随意修改,一般的处理方式为:
·将属性定义为私有属性,即在属性名的前面加上两个下划线
·添加可以供外界调用的两个方法,分别用于设置或者获取属性值
class Dog: def __init__(self, new_name): self.name = new_name self.__age = 0#定义了一个私有的属性,属性的名字是__age def set_age(self,new_age): if new_age>0 and new_age<=100: self.__age = new_age else: self.__age = 0 def get_age(self): return self.__age dog = Dog("小白") #dog.age = -10 #dog.name = "小白" #print(dog.age) dog.set_age(-10) age = dog.get_age() print(age) #dog.get_name() #dog.__age = -10 print(dog.__age)总结:Python中没有任何关键字来区分公有属性和私有属性,它是以属性命名的方法进行区分的,如果属性名的前面加了两个下划线,就表明该属性是私有属性,否则就是公有属性。
私有方法
class Dog: #私有方法 def __send_msg(self): print("------正在发送短信------") #公有方法 def send_msg(self, new_money): if new_money>10000: self.__send_msg() else: print("余额不足,请先充值 再发送短信") dog = Dog() dog.send_msg(100)
继承
单继承
类的继承是指在一个现有类的基础上构建一个新的类,构建出来的新类被称作子类,现有类被称作父类,子类会自动拥有父类的属性和方法。
在Python中,继承使用如下语法格式:
class 子类名(父类名):
如果在类的定义中没有标注出父类,这个类默认是继承自object的。
单继承示例代码如下:
class Cat(object): def __init__(self,color='白色'): self.color = color def run(self): print("-----跑----") class PersianCat(Cat): pass cat =
PersianCat("黑色")cat.run()print(cat.color)
总结:子类继承了父类的color属性和run方法,并且在创建PersianCat类实例额时候,使用的是继承自父类的构造方法。不过,父类的私有属性和私有方法是不会被子类继承的,更不能被子类访问。
继承的注意事项:
class Animal: def __init__(self,color='白色'): self.__color = color #私有属性 def __test(self): #私有方法 print(self.__color) def test(self): print(self.__color) class Dog(Animal): def dogTest1(self): print(self.__color) #访问父类的私有属性 def dogTest2(self): self.__test() #访问父类的私有方法 self.test() #访问父类的私有方法 dog = Dog("深棕色") dog.dogTest1() #运行异常,不能访问父类的私有属性 dog.dogTest2() #运行异常,不能访问父类的私有方法总结:子类没有继承父类的私有方法,而且不能访问父类的私有方法。一般情况下,私有的属性和方法都是不对外公布的,只能用来做其内部的事情。
注意:当在一个类的内部定义了私有方法或者私有属性的时候,Python在运行的过程中,把属性或者方法的名字(不带两个下划线)进行了修饰,即在属性或者方法名称的前面加上“_类名”,导致原有的方法和属性无法访问到。
多次继承示例代码如下:
class Animal: def eat(self): print("-----吃----") def drink(self): print("-----喝----") def sleep(self): print("-----睡觉----") def run(self): print("-----跑----") class Dog(Animal): def bark(self): print("----汪汪叫---") class Xiaotq(Dog): def fly(self): print("----飞----") xiaotq = Xiaotq() xiaotq.fly() xiaotq.bark() xiaotq.eat()多继承
多继承就是子类拥有多个父类,并且具有他们共同的特征,即子类继承了父类的方法和属性。多继承的语法格式如下所示:
class 子类(父类1, 父类2...)
多继承示例代码如下所示:
class Base(object): def test(self): print("----Base") class A(Base): def test1(self): print("-----test1") class B(Base): def test2(self): print("-----test2") class C(A,B): pass c = C() c.test1() c.test2() c.test()总结:子类同时继承了多个父类的方法。
多继承注意点(如果父类中有同名的方法,子类会继承哪一个方法呢?)
class Base(object): def test(self): print("----Base") class A(Base): def test(self): print("-----A") class B(Base): def test(self): print("-----B") class C(A,B): pass #def test(self): # print("-----C") c = C() c.test() print(C.__mro__)
输出结果:
-----A (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
在Python3中,如果子类继承的多个父类间是平行的关系,子类先继承的哪个类就会调用哪个类的方法。如果当前类的继承关系非常复杂,Python中会使用mro算法找到合适的类,可以调用__mro__()方法查看某个类的对象搜索方法时的先后顺序。
重写父类方法与调用父类方法
子类可以对父类中的方法进行重写,使得子类中的方法覆盖掉跟父类同名的方法。需要注意的是,在子类中重写的方法要和父类被重写的方法具有相同的方法名和参数列表。
重写父类的方法示例代码如下所示:
class Animal: def eat(self): print("-----吃----") def drink(self): print("-----喝----") def sleep(self): print("-----睡觉----") def run(self): print("-----跑----") class Dog(Animal): def bark(self): print("----汪汪叫---") class Xiaotq(Dog): def fly(self): print("----飞----") def bark(self): print("----狂叫-----") xiaotq = Xiaotq() xiaotq.fly() xiaotq.bark() xiaotq.eat()
如果子类想要调用父类中被重写的方法,需要使用super方法访问父类中的成员。
调用父类的方法示例代码如下所示:
class Animal: def eat(self): print("-----吃----") def drink(self): print("-----喝----") def sleep(self): print("-----睡觉----") def run(self): print("-----跑----") class Dog(Animal): def bark(self): print("----汪汪叫---") class Xiaotq(Dog): def fly(self): print("----飞----") def bark(self): print("----狂叫-----") #第1种调用被重写的父类的方法(不推荐) #Dog.bark(self) #第2种 super().bark() xiaotq = Xiaotq() xiaotq.fly() xiaotq.bark() xiaotq.eat()
多态
所谓多态:定义时的类型和运行时的类型不一样,此时就称为多态
多态示例代码如下所示:
class Dog(object): def print_self(self): print("大家好,我是xxxx,希望以后大家多多关照....") class Xiaotq(Dog): def print_self(self): print("hello everybody, 我是你们的老大,我是xxxx") def introduce(temp): temp.print_self() dog1 = Dog() dog2 = Xiaotq() introduce(dog1) introduce(dog2)当把dog1作为实参扔给temp时,此时temp调用的是Dog类的print_self方法;当把dog2作为实参扔给temp时,此时temp调用的是Xiaotq类的print_self方法。所以调用同一个方法,出现了两种表现形式,这个过程体现的就是多态。
类属性和实例属性
前面所提到的属性都是实例属性,通过“实例.属性”的方式添加属性和访问属性值。类属性就是累所拥有的属性,它需要在类中显示定义(位于类内部,方法外面),它被所以类的实例对象所共有,在内存中只存在一个副本。
类属性示例:
class Cat(object): #类属性 num = 0实例属性示例:
def __init__(self): #实例属性 self.age = 1
对于公有的类属性,在类的外部可以通过类对象和实例对象访问。
类属性和实例属性示例:
class Cat(object): num = 0 #类属性 def __init__(self): self.age = 1 #实例属性 cat = Cat() print(cat.num) #用实例对象去访问类属性 print(Cat.num) #用类对象去访问类属性(推荐这种方式)
思考:如果在类中有相同名称的类属性和实例属性,那么程序会如果访问呢?看示例代码:
class Cat(object): num = 0 #类属性 #实例方法 def __init__(self): self.age = 1 #实例属性 self.num = 100 #实例属性 cat = Cat() print(cat.num) #用实例对象去访问类属性 print(Cat.num) #用类对象去访问类属性(推荐这种方式)
输出:100
0
总结:如果类属性和实例属性的名字相同,通过实例对象访问属性时会获取实例属性对应的值。所以,当程序要获取类属性的值的时候,通过类获取到的一定是类属性的值。
类方法和静态方法
类方法:可以使用修饰器@classmethod来标识类方法,其语法格式如下:
class 类名: @classmethod def 类方法名(cls): 方法体
在上述格式中,类方法的第一个参数为cls,代表定义类方法的类,可以通过cls访问类的属性。要想调用类方法,既可以通过对象名调用类方法,又可以通过类名调用类方法,这两种方法没有任何区别。
类方法的使用示例如下:
class Test(object): #类属性 num = 0 #类方法 @classmethod def setNum(cls,newNum): cls.num = newNum Test.setNum(300) #通过类名调用类方法 print(Test.num) test = Test() test.setNum(500) #通过类创建的对象,去调用类方法 print(test.num)需要注意的是,类方法是无法访问实例属性的,但是可以访问类属性。
静态方法:可以使用修饰器@staticmethod来标识静态方法,其语法格式如下:
class 类名: @staticmethod def 静态方法名(): 方法体在上述格式中,静态方法的参数列表中没有任何参数。由于静态方法没有self参数,所以它不能访问类的实例属性;静态方法也没有cls参数,所以它也不能访问类属性。静态方法跟定义它的类没有直接的关系,只是起到类似函数的作用。
要想使用静态方法,即可用通过对象名调用,也可以通过类名调用静态方法,这两者没有任何区别。
静态方法的使用示例如下所示:
class Test(object): #静态方法 @staticmethod def printTest(): print("我是静态方法") Test.printTest() #通过类名调用静态方法 test = Test() test.printTest() #通过类创建的对象,去调用静态方法
类的对象可用访问实例方法、类方法和静态方法,使用类可以访问类方法和静态方法。那么,实例方法、类方法和静态方法有什么区别呢?
如果要修改实例属性的值,就直接使用实例方法;如果要修改类属性的值,就直接使用类方法;如果是辅助功能,如打印菜单,这时可以考虑使用静态方法,可以在不创建对象的前提下使用。
注意:使用类名不能访问实例属性或者实例方法。
__new__()方法
前面提到了__init__方法是在创建对象后,系统自动调用对类进行初始化的。而现在讲的__new__方法是用来创建这个对象的。即会先调用__new__方法,然后在调用__init__方法。
__new__方法示例如下所示:
class Person(object): """Silly Person""" def __new__(cls, name, age): print '__new__ called.' return super(Person, cls).__new__(cls, name, age) def __init__(self, name, age): print '__init__ called.' self.name = name self.age = age def __str__(self): return '<Person: %s(%s)>' % (self.name, self.age) if __name__ == '__main__': piglei = Person('piglei', 24) print piglei
执行结果:
piglei@macbook-pro:blog$ python new_and_init.py __new__ called. __init__ called. <Person: piglei(24)>
通过运行这段代码,我们可以看到,__new__方法的调用是发生在__init__之前的。其实当 你实例化一个类的时候,具体的执行逻辑是这样的:
1.p = Person(name, age)
2.首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会 返回Person类的一个实例(通常情况下是使用 super(Persion, cls).__new__(cls, … …) 这样的方式),
3.然后利用这个实例来调用类的__init__方法,上一步里面__new__产生的实例也就是 __init__里面的的 self
所以,__init__ 和 __new__ 最主要的区别在于:
1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。
__new__方法实现单例模式
什么是单例模式?确保一个类中只有一个实例对象,也就是说,当你创建一个类的多个实例对象时,实际上只有第一次的时候创建了实例对象,以后都不在创建实例对象。这就是单例模式。
class Dog(object): pass a = Dog() print(id(a)) b = Dog() print(id(b))
执行结果:
根据输出结果可知,程序创建了多个实例对象(每个对象的id不一样)。
class Dog(object): __instance = None #定义一个私有类属性 def __new__(cls): if cls.__instance == None: #第一次的时候,该变量值为空,所以进入这个分支创建实例对象,并给变量赋值 cls.__instance = object.__new__(cls) return cls.__instance else: #return 上一次创建的对象的引用 return cls.__instance a = Dog() print(id(a)) b = Dog() print(id(b))
根据结果可知,程序只创建了一个实例对象(id都一样),这就是单例模式的实现。
创建单例模式时,怎么让它只执行一次__init__方法呢?
class Dog(object): __instance = None def __new__(cls, name): if cls.__instance == None: cls.__instance = object.__new__(cls) return cls.__instance else: #return 上一次创建的对象的引用 return cls.__instance def __init__(self, name): self.name = name a = Dog("旺财") print(id(a)) print(a.name) b = Dog("哮天犬") print(id(b)) print(b.name)
根据输出结果可知,调用了两次__init__方法,初始化了两次,这是不合理的。那怎么样让它只初始化一次呢?
class Dog(object): __instance = None __init_flag = False def __new__(cls, name): if cls.__instance == None: cls.__instance = object.__new__(cls) return cls.__instance else: #return 上一次创建的对象的引用 return cls.__instance def __init__(self, name): if Dog.__init_flag == False: self.name = name Dog.__init_flag = True a = Dog("旺财") print(id(a)) print(a.name) b = Dog("哮天犬") print(id(b)) print(b.name)
根据输出结果可知,只初始化了一次。