Python面向对象(一)
概述
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,即:将之前实现的代码块复制到现需功能处。
1 while True: 2 if cpu利用率 > 90%: 3 #发送邮件提醒 4 连接邮箱服务器 5 发送邮件 6 关闭连接 7 8 if 硬盘使用空间 > 90%: 9 #发送邮件提醒 10 连接邮箱服务器 11 发送邮件 12 关闭连接 13 14 if 内存占用 > 80%: 15 #发送邮件提醒 16 连接邮箱服务器 17 发送邮件 18 关闭连接
随着时间的推移,开始使用了函数式编程,增强代码的重用性和可读性,就变成了这样:
def 发送邮件(内容) #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
今天我们来学习一种新的编程方式:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
注:Java和C#来说只支持面向对象编程,而python比较灵活即支持面向对象编程也支持函数式编程
面向对象编程
- 面向过程:根据业务逻辑从上到下写代码
- 面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
- 1)解决菜鸟买电脑的故事
第一种方式: 1)在网上查找资料 2)根据自己预算和需求定电脑的型号 MacBook 15 顶配 1W8 3)去市场找到苹果店各种店无法甄别真假 随便找了一家 4)找到业务员,业务员推荐了另外一款 配置更高价格便宜,也是苹果系统的 1W 5)砍价30分钟 付款9999 6)成交 回去之后发现各种问题 第二种方式 : 1)找一个靠谱的电脑高手 2)给钱交易 面向对象和面向过程都是解决问题的一种思路而已 买电脑的第一种方式: 强调的是步骤、过程、每一步都是自己亲自去实现的 这种解决问题的思路我们就叫做面向过程 买电脑的第二种方式: 强调的是电脑高手, 电脑高手是处理这件事的主角,对我们而言,我们并不必亲自实现整个步骤只需要调用电脑高手就可以解决问题 这种解决问题的思路就 是面向对象 用面向对象的思维解决问题的重点 当遇到一个需求的时候不用自己去实现,如果自己一步步实现那就是面向过程 应该找一个专门做这个事的人来做 面向对象是基于面向过程的
2)解决吃啤酒鸭的问题
第一种方式(面向过程): 1)养鸭子 2)鸭子长成 3)杀 4)作料 5)烹饪 6)吃 7)卒 第二种方式(面向对象): 1)找个卖啤酒鸭的人 2)给钱 交易 3)吃 4)胖6斤
需要了解的定义性文字:
面向对象(object-oriented ;简称: OO) 至今还没有统一的概念 我这里把它定义为: 按人们 认识客观世界的系统思维方式,采用基于对象(实体) 的概念建立模型,模拟客观世界分析、设计、实现软件的办法。
面向对象编程(Object Oriented Programming-OOP) 是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
类和对象
一. 类
人以类聚 物以群分。
具有相似内部状态和运动规律的实体的集合(或统称为抽象)。
具有相同属性和行为事物的统称
类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象
二. 对象
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。可以是直接使用的
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
三. 类和对象之间的关系
小总结:类就是创建对象的模板
四、类的构成
类(Class) 由3个部分构成
类的名称:类名
类的属性:一组数据
类的方法:允许对进行操作的方法 (行为)
例子:人类设计,只关心3样东西:
事物名称(类名):人(Person)
属性:身高(height)、年龄(age)
方法(行为/功能):跑(run)、打架(fight)
五、定义一个类
class Cat: #属性 #方法 def eat(self): print("猫在吃鱼……") def drink(self): print('猫正在喝kele。')
- class是关键字,表示类
- 创建对象,类名称后加括号即可
ps:类中的函数第一个参数必须是self(详细见:类的三大特性之封装)
类中定义的函数叫做 “方法”
六、创建一个对象
cat = Cat()
cat.drink()
- 面向对象:【创建对象】【通过对象执行方法】
- 函数编程:【执行函数】
观察上述对比答案则是肯定的,然后并非绝对,场景的不同适合其的编程方式也不同。
总结:函数式的应用场景 --> 各个函数之间是独立且无共用的数据
补充:创建类有两种方式
方式一:普通方式
In [19]: class Love(object): ...: def love(self): ...: print('love') ...: In [20]: f = Love() In [21]: f.love() love
方式二:特殊方式
def love(self): print('love') f = type('Love',(object,),{'func':love}) obj = f() obj.func() out:love In [22]: f Out[22]: <__main__.Love at 0xdb8e81c048> In [23]: Love Out[23]: __main__.Love
七、类的成员
类的成员分为三大类:字段、属性和方法
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
1、字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
- 普通字段属于对象
- 静态字段属于类
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print obj.name # 直接访问静态字段 Province.country 字段的定义和使用
由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:
由上图可知:
- 静态字段在内存中只保存一份
- 普通字段在每个对象中都要保存一份
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
2、方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print(self.name) print '普通方法' @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print('类方法') @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print('静态方法') # 调用普通方法 f = Foo() f.ord_func() # 调用类方法 Foo.class_func() # 调用静态方法 Foo.static_func() 方法的定义和使用
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
3、属性
如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。
对于属性,有以下三个知识点:
- 属性的基本使用
- 属性的两种定义方式
3.1 属性的基本使用
# ############### 定义 ############### 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中没有属性,方法完全可以代替其功能。
实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:
- 根据用户请求的当前页和总数据条数计算出 m 和 n
- 根据m 和 n 去数据库中请求数据
# ############### 定义 ############### class Pager: def __init__(self, current_page): # 用户当前请求的页码(第一页、第二页...) self.current_page = current_page # 每页默认显示10条数据 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val # ############### 调用 ############### p = Pager(1) p.start 就是起始值,即:m p.end 就是结束值,即:n
从上述可见,Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。
3.2 属性的两种定义方式
属性的定义有两种方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器
我们知道Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )
新式类,具有三种@property装饰器
View Code# ############### 定义 ############### 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 方法由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
View Codeclass Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deltter def price(self, value): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价 实例
静态字段方式,创建值为property对象的静态字段
当使用静态字段的方式创建属性时,经典类和新式类无区别
代码:
class Foo: def get_bar(self): return 'wupeiqi' BAR = property(get_bar) obj = Foo() reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值 print(reuslt)
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
class Foo: def get_bar(self): return 'wupeiqi' # *必须两个参数 def set_bar(self, value): return return 'set value' + value def del_bar(self): return 'wupeiqi' BAR = property(get_bar, set_bar, del_bar, 'description...') obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法 obj.BAE.__doc__ # 自动获取第四个参数中设置的值:description...
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 def get_price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price def set_price(self, value): self.original_price = value def del_price(self, value): del self.original_price PRICE = property(get_price, set_price, del_price, '价格属性描述...') obj = Goods() obj.PRICE # 获取商品价格 obj.PRICE = 200 # 修改商品原价 del obj.PRICE # 删除商品原价 实例
注意:Python WEB框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性
class WSGIRequest(http.HttpRequest): def __init__(self, environ): script_name = get_script_name(environ) path_info = get_path_info(environ) if not path_info: # Sometimes PATH_INFO exists, but is empty (e.g. accessing # the SCRIPT_NAME URL without a trailing slash). We really need to # operate as if they'd requested '/'. Not amazingly nice to force # the path like this, but should be harmless. path_info = '/' self.environ = environ self.path_info = path_info self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/')) self.META = environ self.META['PATH_INFO'] = path_info self.META['SCRIPT_NAME'] = script_name self.method = environ['REQUEST_METHOD'].upper() _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', '')) if 'charset' in content_params: try: codecs.lookup(content_params['charset']) except LookupError: pass else: self.encoding = content_params['charset'] self._post_parse_error = False try: content_length = int(environ.get('CONTENT_LENGTH')) except (ValueError, TypeError): content_length = 0 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) self._read_started = False self.resolver_match = None def _get_scheme(self): return self.environ.get('wsgi.url_scheme') def _get_request(self): warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or ' '`request.POST` instead.', RemovedInDjango19Warning, 2) if not hasattr(self, '_request'): self._request = datastructures.MergeDict(self.POST, self.GET) return self._request @cached_property def GET(self): # The WSGI spec says 'QUERY_STRING' may be absent. raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '') return http.QueryDict(raw_query_string, encoding=self._encoding) # ############### 看这里看这里 ############### def _get_post(self): if not hasattr(self, '_post'): self._load_post_and_files() return self._post # ############### 看这里看这里 ############### def _set_post(self, post): self._post = post @cached_property def COOKIES(self): raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '') return http.parse_cookie(raw_cookie) def _get_files(self): if not hasattr(self, '_files'): self._load_post_and_files() return self._files # ############### 看这里看这里 ############### POST = property(_get_post, _set_post) FILES = property(_get_files) REQUEST = property(_get_request) Django源码
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,而【装饰器】方式针对经典类和新式类又有所不同。
八、类的魔法方法及内部执行流程
# -*- coding: utf-8 -*- """ @Datetime: 2018/10/14 @Author: Zhang Yafei """ class Obj(object): """限制对象添加属性""" __slots__ = ['storage', 'stack_func', 'num', 'name'] def __init__(self): """ 创建对象的时候若new返回一个对象会执行此方法 类名()""" object.__setattr__(self, 'storage', {}) print('__init__') def __new__(cls, *args, **kwargs): """创建对象的时候会执行,返回一个对象 应用:单例/rest framework序列化 """ print('__new__') return super(Obj, cls).__new__(cls, *args, **kwargs) def __call__(self): """ 对象()会执行 应用:flask源码请求入口,django请求入口(WSGIHandler.__call__) """ print('__call__') def __str__(self): """ 调用对象会执行此函数 :return: string_obj 返回一个字符串对象 """ return '__str__' def __repr__(self): """ 转化为机器可以解释的语言 case 1: repr(object)时会执行此函数 case 2: 交互模式下打印对象会执行此函数 :return: 用于对象信息的显示 """ return '__repr__' def __getattr__(self, item): """当访问不存在的属性时会调用""" return '__getattr__' def __setattr__(self, key, value): """给对象设置属性的时候会调用""" # self.key = value #容易出现循环调用 print('__setattr__') if key == 'num': object.__setattr__(self, key, value - 100) else: object.__setattr__(self, key, value) def __delattr__(self, item): """删除属性的时候会调用""" print('__delattr__') object.__delattr__(self, item) def __getattribute__(self, item): """访问任何属性的时候都会调用此方法""" print('__getattribute__') return super(Obj, self).__getattribute__(item) def __del__(self): """对象的生命周期执行结束之后执行""" print('__del__') def __setitem__(self, key, value): """obj[key] = value时会调用此方法""" print('__setitem__') self.storage[key] = value def __getitem__(self, key): """obj[key]会调用此方法""" return self.storage.get(key, None) def __delitem__(self, key): """del obj[key]调用""" print('__delitem__') del self.storage[key] def __add__(self, other): return '__add__' def __sub__(self, other): return '__sub__' def __mul__(self, other): return '__mul' def __floordiv__(self, other): return '__floatdiv__' def __mod__(self, other): return '__mod__' def __divmod__(self, other): return '__divmod__' def __pow__(self, power, modulo=None): return '__pow__' obj = Obj() # __new__ __init__ print(obj) # __str__ obj() # __call__ print(Obj.__mro__) # (<class '__main__.Obj'>, <class 'object'>) obj.name = '__dict__' print(obj.__dict__) # print(Obj.__dict__) print(repr(obj)) # __repr__ print(obj.world) # __getattribute__ __getattr__ obj.num = 200 # __setattr__ print(obj.num) # __getattribute__, 100 del obj.num # __delattr__ print(obj.storage) # {} obj['name'] = '张亚飞' # __setitem__ print(obj.storage) # __getattrbute__ __getattrbute__ {'name':'张亚飞'} print(obj['name']) # __getattrbute__ 张亚飞 del obj['name'] # __delitem__ print(obj['name']) # __getitem__, __getitem__, None print(obj + 7) print(obj - 1) print(obj * 1) print(obj // 1) print(obj % 3) print(obj.__divmod__(3)) print(obj.__pow__(2)) # __del__ """ 这里我们想让__setattr__执行默认行为,也就是将value赋值给name,和object对象中的同样方法,做类似的操作。 但是这里我们不调用父类__setattr__的方法来实现,做这样的尝试得到的结果就是,超过循环调用深度,报错。因为 这里在执行初始化方法self.world = world的时候,就会调用__setattr__方法,而这里的__setattr__方法里面的 self.name = value又会调用自身。所以造成了循环调用。所以使用该魔法方法的时候要特别注意。 """ class Friends(object): def __init__(self): self.name = 'zhang' self.age = 23 def func(self): print('__func__') class Xiaoming(Friends): score = 99 def __init__(self): super(Xiaoming, self).__init__() self.run = 200 if __name__ == '__main__': # 一些内置数据类型没有__dict__属性 ll = [] dic = {} num = 3 # print(ll.__dict__) # AttributeError: 'list' object has no attribute '__dict__' # print(dic.__dict__) # print(num.__dict__) # 类的__dict__和对象的__dict__的区别 f = Friends() # 创建实例 print(f.__dict__) f.message = 'hello world' f.func = lambda x:x print(f.__dict__) print(Friends.__dict__) # 继承关系的__dict__ xiaoming = Xiaoming() print(xiaoming.__dict__) print(Xiaoming.__dict__) """ 1. 一些内置数据类型没有__dict__ 2. 实例的__dict__存有与实例相关的实例变量和函数. 类的__dict__则是和实例共享的变量,函数(方法,类属性).注意,类的__dict__并不包含其父类的属性. 3. 对象也有自己的__dict__属性, 存储self.xxx 信息,父子类对象公用__dict__ """ class BAR(object): def __init__(self, cls): self.cls = cls class NEW_OBJ(object): def __new__(cls, *args, **kwargs): # return super(NEW_OBJ, cls).__new__(cls, *args, **kwargs) # <__main__.NEW_OBJ object at 0x000000D445061CF8> # return 123 # 123 # return BAR # <class '__main__.BAR'> # return BAR() # <__main__.BAR object at 0x000000AD77141C50> return BAR(cls) # <__main__.BAR object at 0x0000003BFFA31D68> obj = NEW_OBJ() print(obj) """new方法的返回值决定对象到底是什么"""
# -*- coding: utf-8 -*- ''' # @Datetime: 2018/12/24 # @author: Zhang Yafei ''' """ __new__和__init__的区别 1.__new__:创建对象时调用,会返回当前对象的一个实例 __init__:创建完对象后调用,对当前对象的一些实例初始化,无返回值 2、在类中,如果__new__和__init__同时存在,会优先调用__new__ 3. If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__(). 如果__new__返回一个对象的实例,会隐式调用__init__ If __new__() does not return an instance of cls, then the new instance’s __init__() method will not be invoked. 如果__new__不返回一个对象的实例,__init__不会被调用 """ class Obj(object): def __new__(self, *args, **kwargs): print('__new__') obj = super(Obj, self).__new__(self, *args, **kwargs) print(obj) return obj def __init__(self): print('__init__') # return 1 obj = Obj() class Obj2(object): def __new__(self, *args, **kwargs): print('__new__') def __init__(self): print('__init__') obj2 = Obj2()
九、静态方法和类方法和私有方法
1 静态方法:
静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,主要是一些逻辑属于类,
但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。可以理解为将静态方法
存在此类的名称空间中。事实上,在python引入静态方法之前,通常是在全局名称空间中创建函数。
例子:譬如,我想定义一个关于时间操作的类,其中有一个获得当前时间的函数。
import time class TimeTest(object): def __init__(self, hour, minute, second): self.hour = hour self.minute = minute self.second = second @staticmethod def showTime(): return time.strftime("%H:%M:%S", time.localtime()) print(TimeTest.showTime()) t = TimeTest(2, 10, 10) nowTime = t.showTime() print(nowTime)
如上,使用静态函数,既可以将获得时间的函数功能与实例解绑,我想获得当前时间的字符串时,并不一定需要实例化对象,此时更像是一种名称空间。
我们可以在类外面写一个简单的方法来做这些,但是这样做就扩散了类代码的关系到类定义的外面,这样写就会导致以后代码维护的困难。
静态函数可以通过类名以及实例两种方法调用!
2 类方法
类方法是将类本身作为对象进行操作的方法。他和静态方法的区别在于:不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来。
实例:颜色的动态分配
class ColorTest(object): color = "color" @classmethod def value(self): return self.color class Red(ColorTest): color = "red" class Green(ColorTest): color = "green" g = Green() print(g.value()) print(Green.value()) # 其中,基类做一个抽象共性,对于实际的颜色的值需要结合实际的类进行匹配。
“““ 假设我有一个学生类和一个班级类,想要实现的功能为: 班级类含有类方法: 执行班级人数增加的操作、获得班级的总人数 学生类继承自班级类,每实例化一个学生,班级人数都能增加。 最后,我想定义一些学生,然后获得班级中的总人数。 思考:这个问题用类方法做比较合适,因为我实例化的时学生,但是如果我从学生这一个实例中获得班级总人数是不合理的。 同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。 """ class ClassTest(object): __num = 0 @classmethod def addNum(self): self.__num += 1 @classmethod def getNum(self): return self.__num def __new__(self): ClassTest.addNum() return super(ClassTest, self).__new__(self) class Student(ClassTest): def __init__(self): self.name = '' a = Student() b = Student() print(ClassTest.getNum()) """ 这里我用到魔术函数__new__,主要是为了在创建实例的时候调用人数累加的函数。 类函数可以通过类名以及实例两种方法调用! python2 中,必须总要把一个方法声明为静态的,从而能够不带一个实例而调用它。 python3 中,如果方法只通过类调用,而不需要通过实例调用的话,不用非要声明为静态的 """ class test: def show(): print("show") test.show()
3. 私有方法:方法名前面是双下划线的方法(不能直接被调用)
Python内置类属性 __dict__ : 类的属性(包含一个字典,由类的数据属性组成) __doc__ :类的文档字符串 __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod) __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组) """ class pub(): _name = 'protected类型的变量' __info = '私有类型的变量' def _func(self): print("这是一个protected类型的方法") def __func2(self): print('这是一个私有类型的方法') def get(self): return(self.__info) a = pub() print(a._name) a._func() # print(a.info) # 执行结果: # protected类型的变量 # 这是一个protected类型的方法 # protected类型的变量和方法 在类的实例中可以获取和调用 # # print(a.__info) # # a.__func2() # 执行结果: # File "D:/Python/class/class3.py", line 46, in <module> # print(a.__info) # # AttributeError: pub instance has no attribute '__info' # a.__func2() # AttributeError: pub instance has no attribute '__func2' # 私有类型的变量和方法 在类的实例中获取和调用不到 # 获取私有类型的变量 print(a.get()) # 执行结果:私有类型的变量 # 如果想要在实例中获取到类的私有类形变量可以通过在类中声明普通方法,返回私有类形变量的方式获取
十、私有变量、类变量、成员变量
类变量:定义类的时候直接定义的变量,类本身以及所有对象都可访问
成员变量:构造函数中定义的变量,即成员变量,只有对象可访问
1 class Man(object): 2 #直接定义的类的变量,属于类 3 #其中 gender, avg_height为基本数据类型,immutable 4 #lis为列表类型,为mutable的 5 gender = 'male' 6 avg_height = 1.75 7 lis = ['hello', 'world'] 8 9 def __init__(self, name): 10 self.name = name #name在类的构造函数中定义,是属于对象的变量 11 12 13 a = Man('jason') 14 b = Man('tom') 15 16 #通过一个对象a访问一个变量x,变量的查找过程是这样的: 17 #先在对象自身的__dict__中查找是否有x,如果有则返回,否则进入对象a所属的类A的 18 #__dict__中进行查找 19 20 #对象a试图修改一个属于类的 immutable的变量,则python会在内存中为对象a 21 #新建一个gender变量,此时a就具有了属于自己的gender变量 22 a.gender = 'female' 23 24 #对象b试图修改一个mutable的变量,则python找到类Man的__dict__中的变量lis, 25 #由于lis是可以修改的,因此直接进行修改,而不会给b新生成一个变量。类Man以及类Man 26 #的所有对象都公用这一个lis 27 b.lis = ['fuck', 'world'] 28 29 print a.__dict__ #属于a的变量,有 name, gender 30 print b.__dict__ #属于b的变量,只有name 31 print Man.__dict__ #属于类Man的变量,有 gender,avg_height,lis,但是不包括 name 32 #name是属于对象的变量 33 34 Man.t = 'test' #此时Man的变量又多了t,但是对象a和b中没有变量t。 35 #(这里可以看出,对象的变量和类的变量是分开的) 36 37 print a.gender #female 38 print b.gender #male 39 40 print a.lis #['fuck', 'world'] 41 print b.lis #['fuck', 'world'] 42 43 a.addr = '182.109.23.1' #给对象a定义一个变量,对象b和类Man中都不会出现(解释性语言好随性。。)
私有变量:
1 修改对象的属性有两种方法: 2 对象名.属性名 = 数据 ---->直接修改 3 对象名.方法名() ---->间接修改 4 5 为了更好的保存属性安全,即不能随意修改,一般的处理方式为 6 将属性定义为私有属性 7 添加一个可以调用的方法,供调用
在Python中可以通过在属性变量名前加上双下划线定义属性为私有属性
1、 _xx 以单下划线开头的表示的是protected类型的变量。即保护类型只能允许其本身与子类进行访问。若内部变量标示,如: 当使用“from M import”时,不会将以一个下划线开头的对象引入 。 2、 __xx 双下划线的表示的是私有类型的变量。只能允许这个类本身进行访问了,连子类也不可以用于命名一个类属性(类变量),调用时名字被改变(在类FooBar内部,__boo变成_FooBar__boo,如self._FooBar__boo) 3、 __xx__定义的是特列方法。用户控制的命名空间内的变量或是属性,如init , __import__或是file 。只有当文档有说明时使用,不要自己定义这类变量。 (就是说这些是python内部定义的变量名)
在这里强调说一下私有变量,python默认的成员函数和成员变量都是公开的,没有像其他类似语言的public,private等关键字修饰.但是可以在变量前面加上两个下划线"_",这样的话函数或变量就变成私有的.这是python的私有变量轧压(这个翻译好拗口),英文是(private name mangling.) **情况就是当变量被标记为私有后,在变量的前端插入类名,再类名前添加一个下划线"_",即形成了_ClassName__变量名.**
十一、面向对象三大特性
面向对象三大特性:封装,继承,多态
封装:把函数和全局变量找了个类封装在一起
继承:一个子类继承父类的功能
多态:定义的时候不知道执行哪个功能,直到执行的时候才可以确定调用的是子类还是父类的方法
1、封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
-
- 将内容封装到某处
- 从某处调用被封装的内容
2、继承
在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承足够,如下如所示:
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("---狂叫-----") #第一种被重写的父类的方法 #Dog.bark(self) #第二种 super().bark() class Cat(Animal): def catch(self): print("----抓老鼠---") xiaotq = Xiaotq() xiaotq.fly() xiaotq.bark() xiaotq.eat() wangcai = Dog() wangcai.eat() wangcai.bark() tom = Cat() tom.eat() tom.catch()
class A: def __init__(self): self.num1 = 100 self.__num2 = 200 def test1(self): print("----test1----") def __test2(self): print("-----test2----") def test3(self): self.__test2() print(self.__num2) class B(A): def test4(self): self.__test2() print(self.__num2) b = B() b.test1() #.test2() #私有方法并不会被继承 print(b.num1) #rint(b.__num2) b.test3() #.test4()
重写:子类中一个方法与父类中一个方法名相同,但功能不同,diaoyong子类该方法时会执行该子类中重写的这个方法
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
多继承:广度优先遍历
注:Python2中不声明object默认是经典类,深度优先,python3不管写不写object都是新式类,广度优先
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()
3、多态
定义时的类型和运行时的类型不一样,此时就成为多态。定义的时候不知道执行那个类中的方法,直到执行的时候才知道
class Dog(object): def print_self(self): print("大家好,我是xxxx,希望以后多指教") class Xiaotq(object): def print_self(self): print("hello everybody,我是你的粉丝") def introduce(temp): temp.print_self() dog1 = Dog() dog2 = Xiaotq() introduce(dog1) introduce(dog2)
案例
class SweetPotato: def __init__(self): self.cookedString = "生的" self.cookedlevel = 0 self.condiments = [] def __str__(self): return "地瓜 状态:%s(%d),添加的佐料有:%s"%(self.cookedString,self.cookedlevel,str(self.condiments)) def cook(self,cooked_time): self.cookedlevel += cooked_time if self.cookedlevel >-0 and self.cookedlevel <=3: self.cookedString = "生的" elif self.cookedlevel >=3 and self.cookedlevel <=5: self.cookedString = "半生不熟" elif self.cookedlevel >=5 and self.cookedlevel <=8: self.cookedString = "熟了" elif self.cookedlevel >=8: self.cookedString = "烤糊了" def addCondinments(self,item): self.condiments.append(item) #创建了一个地瓜对象 di_gua = SweetPotato() #开始烤地瓜 di_gua.cook(1) print(di_gua) di_gua.cook(1) print(di_gua) di_gua.addCondinments("大蒜") di_gua.cook(1) print(di_gua) di_gua.cook(1) print(di_gua) di_gua.addCondinments("番茄酱") di_gua.cook(1) print(di_gua) di_gua.addCondinments("孜然") di_gua.cook(1) print(di_gua) di_gua.addCondinments("芥末") print(di_gua)
class Person(object): """人的类""" def __init__(self,name): super(Person,self).__init__() self.name = name self.gun = None #用来保存拿起的这把枪的信息 self.hp = 100 def anzhuang_zidan(self,dan_jia_temp,zi_dan_temp): """把子弹装到弹夹中""" #弹夹.保存子弹(子弹) dan_jia_temp.baocun_zidan(zi_dan_temp) def anhzuang_danjia(self,gun_temp,dan_jia_temp): """枪类""" #枪.保存弹夹(弹夹) gun_temp.baocun_danjia(dan_jia_temp) def naqiang(self,gun_temp): """拿起一把枪""" self.gun = gun_temp def __str__(self): if self.gun: return "%s的血量为:%d,他有枪 %s"%(self.name,self.hp,self.gun) else: if self.hp > 0: return "%s的血量为:%d,他没有枪"%(self.name,self.hp) else: return "%s的血量为0,已挂"%self.name def kou_banji(self,diren): """让枪发射子弹去打敌人""" #枪.开火(敌人) self.gun.fire(diren) def diao_xue(self,sha_shang_li): """根据杀伤力,掉相应的血量""" self.hp -= sha_shang_li class Gun(object): """枪类n""" def __init__(self, name): super(Gun, self).__init__() self.name = name self.danjia = None #用来记录弹夹对象的引用 def baocun_danjia(self,dan_jia_temp): """用一个属性老保存这个弹夹对象的引用""" self.danjia = dan_jia_temp def __str__(self): if self.danjia: return "枪的信息为:%s,%s"%(self.name,self.danjia) else: return "枪的信息为:%s,这把枪中没有弹夹"%(self.name) def fire(self,diren): """枪从弹夹中取出一发子弹,然后让这发子弹射向敌人""" #先从弹夹中取子弹 #弹夹.弹出一颗子弹() zidan_temp = self.danjia.tanchu_zidan() #让这颗子弹去伤害敌人 if zidan_temp: zidan_temp.dazhong(diren) else: print("弹夹中间没有子弹了。。。。") class Danjia(object): """docstring for Danjia""" def __init__(self, max_num): super(Danjia, self).__init__() self.max_num = max_num self.zidan_list = [] #用来记录所有子弹的引用 def baocun_zidan(self,zi_dan_temp): """将这颗子弹保存""" self.zidan_list.append(zi_dan_temp) def __str__(self): return "弹夹的信息为:%d/%d"%(len(self.zidan_list),self.max_num) def tanchu_zidan(self): """弹出最上面的那颗子弹""" if self.zidan_list: return self.zidan_list.pop() else: return "没有子弹" class Zidan(object): """docstring for Zidan""" def __init__(self, sha_shang_li): super(Zidan, self).__init__() self.sha_shang_li = sha_shang_li #这颗子弹的威力 def dazhong(self,diren): """让敌人掉血""" #敌人.掉血(一颗子弹的威力) diren.diao_xue(self.sha_shang_li) def main(): """用来控制整个程序的流程""" #1.创建一个老王对象 laowang = Person("老王") #2.创建一个枪对象 ak47 = Gun("AK47") #3.创建一个弹夹对象 dan_jia = Danjia(20) #4.创建一些子弹 for i in range(15): zi_dan = Zidan(10) #5.老王把子弹装进弹夹 #老王.安装子弹到弹夹中(弹夹,子弹) laowang.anzhuang_zidan(dan_jia,zi_dan) #6.老王把弹夹装进枪 #老王.安装弹夹到枪中(枪,弹夹) laowang.anhzuang_danjia(ak47,dan_jia) #test:测试弹夹的信息 #print(dan_jia) #test:测试枪的信息 #print(ak47) #7.老王拿枪 #老王.拿枪(枪) laowang.naqiang(ak47) #测试老王对象 #print(laowang) #8.创建一个敌人 gebi_laosong = Person("隔壁老宋") #测试敌人对象 #print(gebi_laosong) #9.老王开枪打敌人 #老王.扣扳机(隔壁老宋) i = 0 while i<10: laowang.kou_banji(gebi_laosong) print(laowang) print(gebi_laosong) i+=1 if __name__=="__main__": main()
class Home: def __init__(self,new_area,new_info,new_addr): self.area = new_area self.info = new_info self.addr = new_addr self.left_area = self.area self.contain_items = [] def __str__(self): msg = "房子的总面积是:%d,可用面积是:%d,户型是:%s, 地址是:%s"%(self.area,self.left_area,self.info,self.addr) msg += "当前房子里的物品有%s"%(str(self.contain_items)) return msg def add_item(self,item): #self.left_area -+ item.area #self.contain_items.append(item.name) self.left_area -= item.get_area() self.contain_items.append(item.get_name()) class Bed: def __init__(self,new_name,new_area): self.name = new_name self.area = new_area def __str__(self): return "%s占用的面积是:%d"%(self.name,self.area) def get_area(self): return self.area def get_name(self): return self.name fangzi = Home(129,"三室一厅","北京市 朝阳区 长安街 666号") print(fangzi) bed1 = Bed("席梦思",4) print(bed1) fangzi.add_item(bed1) print(fangzi) bed2 = Bed("三人床",3) print(bed2) fangzi.add_item(bed2) print(fangzi)