python基础之面向对象(二)
一点提醒
首先在使用pickle读取load时,需要先把使用到的类import上,否则会报错.在pycharm中使用时不会报错,但在linux或者cmd时就会报错!必须import.
报错提示:
File "/Users/shane/PycharmProjects/Py_study/Base/S8/s9.py", line 8, in <module>
tt=pickle.load(open('test.pickle','rb'))
FileNotFoundError: [Errno 2] No such file or directory: 'test.pickle'
python编程时和Java C#一点区别
python中的函数参数可以是多种形态的.
Java和C#中,函数参数必须要指定类型;如果想向python中那样多种形态,要将参数指定为某类,参数可为类的子类或者派生类!
面向对象中的类成员
- 字段
- 方法
- 属性
字段
字段分为两种:
- 普通字段
- 静态字段
这里需要注意下的是,编程规范中,类名称一般需要首字母大写!
先来看段小程序吧:
class Province:
country='中国' #静态字段
def __init__(self,name):
self.name=name #普通字段
print(Province.country)
hn=Province('HN')
print(hn.country) #对象能访问,但一般不建议这么使用,建议使用类直接访问
普通字段我们使用的较多,一般是在构造方法时使用.
静态字段在代码加载时已经创建
字段 | 保存位置 | 规则 |
---|---|---|
普通字段 | 保存在对象中 | 只能用对象去访问 |
静态字段 | 保存在类中(节约内存空间) | 对象/类均可访问,但一般使用类访问,万不得已才使用对象访问) |
方法
所有方法都属于类,类基本可以分为三中类型:
- 普通方法
- 静态方法
- 类方法
方法 | 调用方式 | 特征 |
---|---|---|
普通方法 | 由对象去调用执行,属于类 | 至少一个self,对象调用 |
静态方法 | 属于类,但通过类来调用,不依赖于任何对象,方法内部不需要对象封装的值时,可使用静态方法 | 任意参数,没有self,上面添加@staticmethod,类似装饰器的东东 |
类方法 | 静态方法的一种特殊形式,由类调用 | 至少一个cls参数,上面添加classmethod |
来看两个例子吧:
静态方法:
class Province:
country='中国' #静态字段
def __init__(self,name):
self.name=name #普通字段
@staticmethod #静态方法
def show(arg1,arg2):
print(arg1,arg2)
print(Province.country)
hn=Province('HN')
print(hn.country)
Province.show(123,456)
类方法:
class Province:
country='中国' #静态字段
def __init__(self,name):
self.name=name #普通字段
@staticmethod #静态方法
def show(arg1,arg2):
print(arg1,arg2)
@classmethod
def f2(cls):
print(cls)
Province.f2()
out:
<class '__main__.Province'>
属性
属性的特征:具有方法的写作形式,具有字段的访问形式.可取值,可设置,可删除.
先来看看属性是个什么东西吧:
class Pager:
def __init__(self,all_count):
self.all_count=all_count
@property #定义属性
def all_pager(self):
a1,a2=divmod(self.all_count,10)
if a2==0:
return a1
else:
return a1+1
@all_pager.setter #赋值属性,将value赋值给方法的参数
def all_pager(self,value):
print(value)
@all_pager.deleter #删除属性
def all_pager(self):
print('del all_pager')
obj=Pager(101)
ret=obj.all_pager #不需要加括号
print(ret)
obj.all_pager=102
del obj.all_pager
属性的基本使用
属性的定义和调用需要注意以下几点:
- 定义时,在普通方法的基础上添加@property装饰器
- 定义时,属性仅有一个self参数
- 调用时,无需括号,obj.all_paper
属性存在的意义:访问属性时,可以制造出和访问字段完全相同的假象,由于属性是由方法变种而来,如果python中没有属性,完全可以由方法来替代.
属性的两种定义方式
python3中全都是新式类,有三种@property装饰方式:
- @property
- 方法名.setter
- 方法名.deleter
其实就像上面的例子.
还有一种方式是静态字段方式.创建值为property的对象的静态字段.
来看个例子:
class Pager:
def __init__(self,name):
self.name=name
def f1(self):
return 123
def f2(self,value):
print(value)
def f3(self):
print('del....')
foo=property(fget=f1,fset=f2,fdel=f3)
obj=Pager(110)
ret=obj.foo #调用fget
print(ret)
obj.foo=778 #调用fset
del obj.foo #调用fdel
输出结果:
123
778
del....
其中fget,fset,fdel为默认参数.
类成员修饰符
每个类成员的修饰符有两种:
- 公有成员:内部外部都能访问
- 私有成员:字段前面加两个_,比如:__cc,只有自己的类内部能访问,继承的父类,子类均不能访问.
class Foo:
__cc='cccccccccc'
def __init__(self,name):
self.__name=name
def f1(self):
print(Foo.__cc)
def f2(self):
print(self.__name)
@staticmethod
def __f3():
print('this is f3')
@staticmethod
def f4():
Foo.__f3()
# def
obj=Foo('alex')
# obj.f1() #正确
# obj.f2() #正确
# obj.__f3() #报错
obj.f4() #正确
# Foo.__f3() #报错
Foo.f4()
print(obj._Foo__cc) #能显示,但不建议这么做,万不得已不要这么使用
# print(obj.name) #报错
成员 | 字段/方法/属性 | 访问方式 |
---|---|---|
公有成员 | 静态 | 类可以访问;类内部可以访问;派生类中可以访问 |
公有成员 | 普通 | 对象可以访问;类内部可以访问;派生类中可以访问 |
私有成员 | 静态 | 仅类内部可以访问 |
私有成员 | 普通 | 仅类内部可以访问 |
print(obj._Foo__cc)
这种方式可以强制访问私有成员,但不符合规范...
类的特殊成员
python的类成员中有一部分特殊的存在.
-
__doc__ 表示类的描述信息,即注释
-
__module__ 表示当前操作的对象属于哪个模块
-
__class__ 表示当前操作的对象的类是什么
-
__init__ 构造方法
-
__del__ 析构方法,当对象在内存中被释放的瞬间执行(此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。)
-
__call__ 对象后面加括号,触发执行,例如:
class Foo: def __init__(self,name,age): self.name=name self.age=age def __call__(self, *args, **kwargs): #加括号执行 print("__call__") def __str__(self): return '%s-%s'%(self.name,self.age) a=Foo('alex',19) a()
out:
__call__ alex-18 __call__
-
__str__ 覆盖显示内存地址的内容,如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值。
-
__dict__ 类或对象中的所有成员,如果是类,返回的是对象的所有字段,如果是对象,返回所有成员,并且无论类或者对象,全部返回数据为字典类型.
class Foo: def __init__(self,name,age): self.name=name self.age=age def __call__(self, *args, **kwargs): #加括号执行 print("__call__") def __str__(self): return '%s-%s'%(self.name,self.age) a=Foo('alex',19) # a() # print(a) print(a.__dict__) print(Foo.__dict__)
out:
{'name': 'alex', 'age': 19} {'__doc__': None, '__str__': <function Foo.__str__ at 0x10127b7b8>, '__call__': <function Foo.__call__ at 0x10127b730>, '__init__': <function Foo.__init__ at 0x10127b6a8>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__dict__': <attribute '__dict__' of 'Foo' objects>} alex-18 __call__
9. \_\_getitem__、\_\_setitem__、\_\_delitem__ 用于索引操作,对象后面加中括号"[]",将中括号中的参数传给item,来两个例子:
```python
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def __getitem__(self, item):
if type(item)==str:
print(798)
return 798
elif type(item)==slice:
print(item.start)
print(item.stop)
print(item.step)
def __setitem__(self, key, value):
if type(key)==str:
print('setitem')
elif type(key)==slice and type(value)==list:
print(key.start)
print(key.stop)
print(key.step)
print(value)
def __delitem__(self, key):
if type(key)==str:
print('delitem')
elif type(key)==slice:
print(key.start)
print(key.stop)
print(key.step)
a=Foo('cc',23)
res=a[1:4:12]
print(res)
a[1:4]=[2,3,4,5]
del a[1:5]
```
输出:
```python
798
798
setitem
delitem
```
10. \_\_getslice__、\_\_setslice__、\_\_delslice__ 用于分片操作,看个例子:
```python
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def __getitem__(self, item):
if type(item)==str:
print(798)
return 798
elif type(item)==slice:
print(item.start)
print(item.stop)
print(item.step)
def __setitem__(self, key, value):
if type(key)==str:
print('setitem')
elif type(key)==slice and type(value)==list:
print(key.start)
print(key.stop)
print(key.step)
print(value)
def __delitem__(self, key):
if type(key)==str:
print('delitem')
elif type(key)==slice:
print(key.start)
print(key.stop)
print(key.step)
a=Foo('cc',23)
res=a[1:4:12] #自动执行getslice
print(res)
a[1:4]=[2,3,4,5] #自动执行setslice
del a[1:5] #自动执行delslice
```
out:
```python
1
4
12
None
1
4
None
[2, 3, 4, 5]
1
5
None
```
11. \_\_iter__ 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 \_\_iter__,如果对象想被迭代,就得用此方法.
```python
class Foo:
def __iter__(self):
yield 1
yield 2
a=Foo()
for item in a:
print(item)
```
out:
```python
1
2
```
###自己写一个有序字典
利用上面的知识,我们可以自己写一个有序字典:
```python
class Mydict(dict):
def __init__(self):
self.li=[]
super(Mydict,self).__init__()
def __setitem__(self, key, value):
self.li.append(key)
super(Mydict,self).__setitem__(key,value)
def __str__(self):
temp_list=[]
for key in self.li:
value=self.get(key)
temp_list.append('"%s":%s'%(key,value))
temp_str='{'+','.join(temp_list)+'}'
return temp_str
d=Mydict()
d['a']=111
d['b']=222
print(d)
输出:
{"a":111,"b":222}
其他相关
isinstance(obj,cls) 检查obj是否是cls创建的对象
issubclass(cls1,cls2) 检查cls1是否是cls2的子类
super(cls,self).func() 主动执行父类的方法,看个例子:
class c1:
def f1(self):
print('c1.f1')
class c2(c1):
def f1(self):
super(c2,self).f1()
print('c2.f1')
#如果想主动执行c2父类的f1(),可以这么写
a=c2()
a.f1()
out:
c1.f1
c2.f1
此方法一般用来在不改变原代码的基础上做修改时使用此类方法即可.
单例模式
总结上面的知识,我们写一个单例模式吧,顾名思义,单例模式就是此模式只能创建一个实例,第一次创建一个模式,如果第二次创建是,返回还是第一个创建的实例或者对象,看代码吧:
class Foo:
instance=None
def __init__(self,name):
self.name=name
@classmethod
def get_instance(cls,value): #一般没value
if cls.instance:
return cls.instance
else:
obj=cls(value) #一般设定死
cls.instance=obj
return obj
obj1=Foo.get_instance('alex')
print(obj1)
obj2=Foo.get_instance('cc')
print(obj2)
输出:
<__main__.Foo object at 0x101a78ac8>
<__main__.Foo object at 0x101a78ac8>