python类和对象理解
Posted chxb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python类和对象理解相关的知识,希望对你有一定的参考价值。
类的概念
类(class)抽象的概念,比如说人类、鸟类、水果、是一个总的称呼,没有具体到某个物体;
对象(object,指具体实例,instance);
类定义的语法:
class 类名: 执行语句 类变量 类方法
类最重要的两部分就是类变量和类方法,类成员之间的可以相互调用。
程序可以在类中给新变量赋值就是增加类变量,可以通过del语句删除已有类的变量。
在__init__构造函数(构造函数后面会说到)里面的是实例变量,程序可以任何位置(类里面或者类外面)增加实例变量,删除则用del语句。
在实例方法中有一个特别的方法 :__init__ ,这个方法被称为构造方法 。 构造方法用于构造该类的对象, Python 通过调用构造方法返回该类的对象 。 python提供一个功能就是:若开发者没有定义构造函数,python会自动提供一个只包含self参数默认的构造方法。
class Bird: ‘这是学习python的第一个类‘ eyes = "two" def __init__(self, color,feet): ‘为python对象增 ‘ self.color = color self.feet = feet def call(self,cd): print("This bird:",cd)
上面的 Bird类定义了 一个构造方法,该构造方法只是方法名比较特殊:__init__ ,该方法的第一个参数同样是 self,被绑定到构造方法初始化的对象 。和函数定义文档类似,类文档用字符串即可。该文档同样被放在类声明之后、类体之前。
下面是调用类的构造方法创建对象。第二行是调用实例方法。
xique = Bird("green","two") xique.call("gezhi")
#打印实例变量 print(xique.color,xique.feet) #访问实例变量,对实例变量赋值 xique.color = "brown" #增加实例变量 xique.skin = ‘yellow‘ #打印编辑后的实例变量 print(xique.color,xique.feet,xique.skin) #调用实例方法call(),第一个参数仍是self,self代表实例本身, #是自动绑定的,不需要再次输入,因此调用实例方法,只需再输入一个参数即可 xique.call("zhizhizhizhi")
同一个类的多个对象具有相同的特征,类用定义多个对象的相同特征。类不是一个具体的实体,对象才是一个具体的实体
class Person: ‘这是学习python的第二个类‘ hair = ‘black‘ def __init__(self,name = "Will",age = 8): ‘下面对Person对象增加两个实例变量‘ self.name = name self.age = age ‘定义一个say()方法‘ def say(self,content): print(content)
给对象增加一个实例变量
# 增加一个skills实例变量 p.skills = [‘programming‘,‘writing‘] print(p.skills) #删除p对象的name实例变量,而不是删除类中变量,新建一个对象,name实例变量还是构造函数默认的。 del p.name # print(p.name) 会报错
给对象增加类方法
1 #动态增加方法 2 def info(self): 3 print("-----info函数-----",self) 4 print("动态增加方法") 5 return None 6 p.foo = info # foo实例方法名,info是我们在外面定义的方法,当然二者名字可以相同 7 8 ‘‘‘方法增加,只用了变量名,后面并没有加括号方法动态增加,第一个参数并没有绑定给调 9 用类的对象,所以我们需要手动把第一个参数输入,第一个参数是实例本身,可用实例名代表‘‘‘ 10 print(p.foo(p)) 11 12 #如果想自动绑定,调用方法时,不想输入self参数,可以用type模块包装 13 #重新写一个函数 14 def message(self,comment): 15 print("我的个人信息是 %s " % comment) 16 return None 17 18 p2 = Person() 19 p2.message = message 20 21 from types import MethodType 22 p2.message = MethodType(p2.message,per) 23 24 #综上,整队某个对象动态增加的类变量和类方法,只能应用此对象,其他对象需重新增加
实例方法调用另一个实例方法
1 class Dog: 2 skin = "white" 3 def __init__(self,name = ‘Aled‘): 4 self.name = name 5 def jump(self): 6 print("执行jump方法") 7 def run(self): 8 self.jump() #此处不能写成jump()必须有self,通过调用实例对象的方法 9 print("调用run方法") 10 11 g = Dog() 12 g.run() #通过run()方法,调用了jump()方法
类中self参数就是实例本身,可以自动绑定。
1 #在构造方法中,self参数(第一个参数)表示该构造函数正在初始化的对象 2 class InConstructor: 3 def __init__(self): 4 #在构造方法中定义一个foo变量(局部变量),临时的 5 foo = 1 #这是一个局部变量,不是实例变量,外界无法访问 6 print(type(foo)) 7 print(foo) 8 #把构造方法正在初始化的foo变量编程实例变量,并且重新复制 9 self.foo = 5 10 p = InConstructor() 11 print(p.foo) 12 13 #自动绑定的self参数不依赖具体的调用方式,不管是以方法调用还是函数调用,self参数用一样可以自动绑定 14 class User: 15 def test(self): 16 print("self参数:",self) 17 u = User() 18 u.test() #self参数: <__main__.User object at 0x013ADCF0> User对象在内存... 19 20 #将User对象的test方法赋值给foo变量 21 foo = u.test #只需将名字赋值,不要加括号 22 #通过foo变量调用test()方法 23 foo() #效果 一样,因为foo也是指向u.test 24 #self参数可以作为实例方法返回值
self可以作为变量来访问,或者作为实例方法的返回值
当 self 参数作为对象的默认引用时,程序可以像访问普通变量一样来访 问这个self 参数,甚至可以把 self 参数当成实例方法的返回值 。 看下面程序 。
1 class ReturnSelf: 2 def grow(self): 3 if hasattr(self,‘age‘): 4 self.age += 1 5 print("有age变量") 6 else: 7 self.age = 22 8 print("无age变量") 9 return self #返回调用该方法的实例对象 10 def isnotexist(self): 11 ‘实例变量不一定非要在构造方法中定义,也可以在类外,或者类里的实例方法中定义‘ 12 print(self.age) 13 14 rs = ReturnSelf() 15 print(rs.grow().age) # 22 16 rs.isnotexist() #22 17 rs.grow().grow().isnotexist() #返回值是self实例本身,然后可以多次调用实例方法或变量
类也能调用实例方法
类名.method(参数)
类名.变量名
1 #类调用实例方法 类名.method 2 3 #前面都是通过创建对象,通过对象调用实例方法 4 #类很大程度上类似命名空间和定义变量和定义函数没有什么不同 5 6 #定义全局空间的foo函数 7 def foo(): 8 print("全局空间的foo方法") 9 #定义全局空间bar变量 10 bar = 20 11 class Bird: 12 #定义Bird空间的foo函数 13 def foo(self): 14 print("Bird空间的foo方法") 15 #定义Bird空间的bar变量 16 bar = 200 17 #调用全局空间的函数和变量 18 foo() 19 print(bar) 20 #调用Bird空间的函数和变量 21 # Bird.foo() 这样会报错。缺少self参数 22 print(Bird.bar)
直接类名+方法会报错,方法里面必须手动添加参数
1 class User: 2 def walk(self): 3 print(self,‘正在慢慢走‘) 4 #通过类调用实例方法 5 # User.walk() 这样报错 TypeError
u = User()
User.walk(u) #显式方法的第一个参数绑定参数值
这样的调用效果等同于u.walk()
实际上,当通过 User 类调用 walk()实例方法时, Python只要求手动为第一个参数绑定参数值,并不要求必须绑定 User 对象,因此也可使用如下代码进行调用 。
1 User.walk("任意输入都有可以,不一定非得是self参数")
如果传入两个参数呢
1 class Peaple: 2 def walk(self,name = "Will"): 3 print(name,"坐着",self) 4 #和普通函数调用没啥区别,就当self是一个变量名,参数随便输入 5 Peaple.walk("吃饭") 6 Peaple.walk("吃饭","Alex")
类方法和静态方法
Python其实可以支持类方法定义,区别前面的实例方法,同时只是静态方法定义,类方法和静态方法类似,都可以通过类调用(同时也支持对象调用)区别在于类方法第一个参数为cls,会自动绑定到类,而静态方法不会自动绑定到类
class Bird: #使用classmethod是类方法 @classmethod def fly(cls): print(‘类方法fly:‘,cls) #使用staticmethod修饰的是静态方法 @staticmethod def info(p): print(‘静态方法info:‘,p) #调用类方法,类会自动绑定到第一个参数cls Bird.fly() #调用静态方法,不会自动绑定,意思是第一个参数必须手动输入 Bird.info("真麻烦") #创建Bird对象 b = Bird() #使用对象调用fly类方法,其实还是使用类调用 #因此第一个参数 依然自动绑定到Bird类 b.fly() #使用对象调用info静态方法,其实还是使用类调用 b.info(‘fkit‘)
装饰器
1 #装饰器 2 #funA装饰funB,funB作为参数引入到funA中,同时funA返回值就是修饰后的返回值 3 def funA(funB): 4 print("A") 5 funB() 6 return "最终修饰结果" 7 def funB(): 8 print("B") 9 funB() 10 11 @funA 12 def funB(): 13 print("B") 14 print(type(funB)) #这里是函数名,不是函数调用 15 print(funB) #装饰函数修饰后,通过被修饰函数名查看返回值
这个函数装饰器导致被修饰的函数变成了字符串,那么函数装饰器有什么用?别忘记了,被修饰的函数总是被替换成@符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于@符号所引用的函数的返回值决定一一如果@符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数 。
1 def foo(fn): 2 #定义一个嵌套函数 3 def bar(*args): 4 print("===1===",args) 5 n = args[0] 6 print("===2===",n * (n -1)) 7 #查看传递给foo函数的fn函数 8 print(fn.__name__) 9 fn(n * (n -1)) 10 print("*" * 15) 11 return fn(n * (n -1)) 12 return bar 13 14 @foo 15 def my_test(a): 16 print("===my_test函数===",a) 17 print(my_test) #返回值是bar函数<function foo.<locals>.bar at 0x032C3D20> 18 my_test(10) #意思就是my_test函数被bar函数替换,调用my_test函数就是调用bar函数 19 my_test(6,5)
上面装饰结果相当于foo(my_test),my_test函数会被替换(装饰)成foo(my_test)的返回值,他的返回值是bar函数,因此funB(my_test函数)就是bar函数
通过修饰函数,也可以在修饰函数中添加权限检查,逻辑验证,异常处理
下面将示范通过函数修饰器给函数添加权限检查的功能:
1 def auth(fn): 2 def auth_fn(*args): 3 print("----模拟执行权限检查-----") 4 #回调被修饰的目标函数 5 fn(*args) #作为函数参数时前面必须有*,如果在函数里面的参数则为args 6 return auth_fn 7 @auth 8 def bedecorated(a,b): 9 print("执行bedecotated函数,参数a:%s,参数b:%s" % (a, b)) 10 11 #调用bedecorated函数,其实就是调用修饰后返回的auth_fn函数 12 13 bedecorated(4,5)
类命名空间
1 #类命名空间 2 global_fn = lambda p: print("执行lambda表达式,P参数:",p) 3 class Category: 4 cate_fn = lambda p:print("执行lambda表达式,p参数",p) 5 #调用全局的global_fn,为参数p传入参数值 6 global_fn(‘fkit‘) 7 c = Category() 8 c.cate_fn()
综上,在全局命名空间调用,类命名空间的lambda函数也可以调用,通过类对象调用lambda函数相当于调用类方法,python同样会自动为该刚方法绑定第一个参数值(self),也就是实例对象本身
成员变量
1 class Address: 2 detail = ‘广州‘ 3 post_code = ‘2019723‘ 4 def info(self): 5 #尝试直接访问类变量 6 #print(detail) 报错 7 print(Address.detail) 8 print(Address.post_code) 9 Address.info(32) #通过类调用方法,需要手动输入第一个参数 10 #通过类来访问Address类的类变量 11 print(Address.detail) 12 addr = Address() 13 addr.info() 14 #修改Address的类变量 15 Address.detail = "佛山" 16 Address.post_code = ‘2018723‘ 17 addr.info()
在类命名空间内定义的变量就属于类变量 , Python 可以使用类来读取、修改类变量。 对于类变量而言,它们就是属于在类命名空间内定义的变量 ,因此程序不能直接访问这些变量,程序必须使用类名来调用类变量。不管是在全局范围内还是函数内访问这些类变量,都必须使用类名进行访问。
1 #python也可以使用对象访问类变量,建议使用类访问类变量 2 class Record: 3 #定义两个类变量 4 item = ‘鼠标‘ 5 date = ‘2019-07-23‘ 6 def info(self): 7 print(‘info方法:‘,self.item) 8 print(‘info方法:‘,self.date) 9 rc = Record() 10 print(rc.item) 11 print(rc.date) 12 rc.info() 13 14 #修改Record类的两个类变量 15 Record.item = "键盘" 16 Record.date = ‘2020-01-01‘ 17 rc.info()
Python 允许通过对象访问类变量 ,但如果程序通过对象尝试对类变量赋值,此时性质就变了一Python 是动态语言,赋值语句往往意味着定义新变量。因此,如果程序通过对象对类变量赋值,其实不是对“类变量赋值”,而是定义新的实例变量 。例如如下程序 。
1 class Inventory: 2 #定义两个变量 3 quantity = 2000 4 item = ‘鼠标‘ 5 #定义实例方法 6 def change(self,item,quantity): 7 self.item = item 8 self.quantity = quantity 9 #创建Inventory对象 10 iv = Inventory() 11 iv.change(‘显示器‘,500) 12 #访问iv的item和quantity实例变量 13 print(iv.item) #显示器 14 print(iv.quantity) #500 15 16 #访问Inventotry的item和quantity类变量 17 print(Inventory.item) #鼠标 18 print(Inventory.quantity) #2000
通过类修改类变量的值,实例变量不会受到影响
1 Inventory.item = ‘类变量item‘ 2 Inventory.quantity = ‘类变量quantity‘ 3 #访问iv对象的实例变量item和quantity 4 print(iv.item) #显示器 5 print(iv.quantity) #500
同样修改实例变量的值,这种修改也不影响类变量或者其他对象的实例变量。
1 iv.item = ‘iv实例变量item‘ 2 iv.quantity = ‘iv实例变量quantity‘ 3 print(Inventory.item) 4 print(Inventory.quantity) 5 iv2 = Inventory() 6 print(iv2.item) #类变量item,前面修改了类变量,此处没有调用change方法创建新的实例变量,所以这样调用的是类变量 7 #新对象创建实例变量item ,quantity 8 iv2.change(‘音响‘,600) 9 print(iv2.item) #音响,这次就不是类变量了,因为创建的实例变量,python内在机制,实例可以调用类变量,其实还是通过类访问的类变量
使用property函数定义属性
1 class Retangle: 2 def __init__(self,width,height): 3 self.width = width 4 self.height = height 5 def setsize(self,size_): 6 self.width,self.height = size_ 7 def getsize(self): 8 return self.width,self.height 9 def delsize(self): 10 self.width,self.height = 0,0 11 size = property(getsize,setsize,delsize,‘描述事物的属性‘) 12 #访问size的说明文档 13 print(Retangle.size.__doc__) 14 #通过内置的help函数查看说明文档 15 help(Retangle.size) 16 rect = Retangle(4,3) 17 print(rect.size) 18 rect.setsize((7,9)) 19 print(rect.size) 20 rect.size = 9,7 21 print(rect.width,rect.height) 22 del rect.size 23 print(rect.width,rect.height) 24 25 class Retangle: 26 def __init__(self,width,height,length): 27 self.width = width 28 self.height = height 29 self.length = length 30 def getsize(self): 31 return self.width,self.height,self.length 32 def setsize(self,size): 33 self.width,self.height,self.length = size 34 def delsize(self): 35 self.width,self.height,self.length = 0,0,0 36 size = property(getsize,setsize,delsize,‘描述事物的属性‘) 37 rec = Retangle(88,56,72) 38 print(rec.size) 39 rec.size = 85,45,23 40 print(rec.size) 41 print(rec.width,rec.length,rec.height) 42 del rec.size 43 print(rec.size) 44 45 46 class User: 47 def __init__(self,first,last): 48 self.first = first 49 self.last = last 50 def getfullname(self): 51 return self.first + ‘,‘ + self.last 52 def setfullname(self,fullname): 53 name = fullname.split(‘,‘) 54 self.first = name[0] 55 self.last = name[1] 56 fullname = property(getfullname,setfullname) 57 u = User(‘孙‘,‘悟空‘) 58 print(u.fullname) 59 print(u.first,u.last) 60 u.fullname = ‘smith,jackey‘ 61 print(u.fullname) 62 print(u.first,u.last)
隐藏和封装
1 class User: 2 def __hide(self): 3 print("示范隐藏的方法") 4 def getname(self): 5 return self.__name 6 7 def setname(self,name): 8 if len(name) < 3 or len(name) > 8: 9 raise ValueError("输入长度在3-8之间的") 10 self.__name = name 11 name = property(getname,setname) #参数必须先 12 13 def getage(self): 14 return self.__age #可以和property的属性区分开来 15 def setage(self,age): 16 if age < 11 or age > 70: 17 raise ValueError("年龄在11到70岁之间") 18 self.__age = age 19 age = property(getage,setage) 20 21 u = User() 22 u.name = "fuck" 23 print(u.name) 24 print(u.getname()) 25 u.age = 56 26 print(u.getage())
set和get函数方法顺序无关,但是property函数内参数,必须是先读取后写入的顺序。
继承
#继承 class Fruit: def info(self,weight): #如果后面有参数,函数内就必须初始化,如果没参数,后面初始化不同写 self.weight = weight print("this fruit weigh %s" % (self.weight)) class Food: def taste(self): print("不同食物,口味不同") class Apple(Fruit,Food): pass a = Apple() a.info(25) a.taste()
1 #当父类方法名字重合,选择第一个 2 class Item: 3 def info(self): 4 print("这是一个商品") 5 class Product: 6 def info(self): 7 print(‘这是一个工艺‘) 8 9 class Mouse(Product,Item): #父类顺序,如果有相同的方法,先调用一个参数 10 pass 11 12 m = Mouse() 13 m.info()
父类方法重写
1 class Bird: 2 def fly(self): 3 print("我在天空里自由自在地飞翔") 4 class Ostrich(Bird): 5 #重写Bird类的fly()方法 6 def fly(self): 7 print("我只能在地上奔跑") 8 #创建Ostrich对象 9 os = Ostrich() 10 #执行Ostrich对象的fly(),将输出“我只能在地上奔跑” 11 os.fly()
子类重写父类方法,那如何调用父类被重写的方法。
1 class BaseClass: 2 def foo(self): 3 print("父类中定义的foo方法") 4 class SubClass(BaseClass): 5 def foo(self): 6 print("子类中定义的foo方法") 7 def bar(self): 8 print("执行bar方法") 9 self.foo() 10 #通过类名调用父类被重写的方法 11 BaseClass.foo(self) 12 sc = SubClass() 13 sc.bar()
使用super函数调用父类构造方法
1 class Employee: 2 def __init__(self,salary): 3 self.salary = salary 4 def work(self): 5 print("普通员工正在写代码,工资是:",self.salary) 6 class Customer: 7 def __init__(self,favorite,address): 8 self.favorite = favorite 9 self.address = address 10 def info(self): 11 print("我是一个顾客,我的爱好是:%s,地址是%s" % (self.favorite,self.address)) 12 #manager 继承了Employee,Customer 13 class Manager(Customer): 14 def __init__(self,favorite,address): 15 print("manager的构造方法") 16 #通过super函数调用父类的构造方法 17 super(Manager,self).__init__(favorite,address) 18 19 20 21 m = Manager("IT","beijing") 22 23 m.info() 24 25 class Fooparent: 26 def __init__(self): 27 self.parents = ‘I\‘m the parent‘ # 5 28 print("Parent11") # 1 29 def bar(self,message): 30 print("%s from Parent"% message) # 3 31 class FooChild(Fooparent): 32 def __init__(self): 33 super(FooChild,self).__init__() 34 print("child22") # 2 35 def bar(self,message): 36 super(FooChild,self).bar(message) 37 print("Child bar function") #4 38 print(self.parents) 39 if __name__ == "__main__": 40 foochild = FooChild() #创建一个子类对象 41 foochild.bar("helloworld") 42 43 #从上面可见先执行父类构造函数的打印函数,在执行子类打印函数,然后根据调用执行父类函数
python动态属性与slots
class Cat: def __init__(self,name): self.name = name def walk_func(self): print("%s慢慢走过每一片草地"% self.name) d1 = Cat(‘Marry‘) d2 = Cat(‘Kitty‘) Cat.walk = walk_func d1.walk() d2.walk() #__slot__限制动态添加的属性和方法 class Dog: __slots__ = (‘walk‘,‘age‘,‘name‘) def __init__(self,name): self.name = name def test(): print("预先定义好的test方法") d = Dog(‘Snoogy‘) d.age = 5 print(d.age) # d.weight = 24报错 Dog.walk = walk_func d.walk() Dog.bar = lambda self:print("abc") d.bar()
type函数定义类
def fn(self): print("fn函数") #使用type函数定义Dog类 Dog = type(‘Dog‘,(object,),dict(walk = fn,age = 6)) #创建dog对象 d = Dog() #分别查看d.dog的类型 print(type(d)) print(type(Dog)) print(type(Dog())) print(type(d.walk)) d.walk() print(d.age)
以上是关于python类和对象理解的主要内容,如果未能解决你的问题,请参考以下文章