python基础总结
聊一聊Python中的类
什么是Python的类,很多人肯定会随手打出
class Student(object):
def __init__(self):
pass
jack = Student()
Helen = Student()
完事了,但其实不然,Python的类还是有很多值得考虑和记录的地方
基础向
如上所说,我们先来理一理Python类的基础,首先既然说到类,我们就离不开对象这个概念,Python是一门解释型动态语言,在Python中处处皆对象。既然有了对象,我们当然要具有生产这些对象的模板,我们把它称为类。在Python中,Class简称是抽象类的统称,比如上方的Student就是一个抽象的概念,他是一个模板。那么jack和Helen则是他的实例。于是面向对象中,类(Class)和实例(Instance)的概念就出来了,记住类只是一个抽象的概念,是模板。而实例则是由这些模板生产出来的实例。
继承最基础的类
看到现在我们应该有一个概念,也就是说我们创造一个类,我们可以使用他创造出多个实例。那么多个类,就可以创造出多个多个实例。那么其实这些类都继承自一个最基础的类object,在Python2.4-3中根据是否继承object类来区分新式类和经典类,但是在python3中,已经默认继承Object类了,所以你完全可以这样写
class Student:
def __init__(self):
pass
类的内存与实例
既然类是创建实例的模板,那么必须有存放类的内存的地方
class Student(object):
def __init__(self):
pass
if __name__ == "__main__":
print(Student)
print(Student())
a = Student()
print(a)
输出的结果如下
PS E:app> & C:/Users/alpaca/AppData/Local/Programs/Python/Python37/python.exe e:/app/demo1.py
<class '__main__.st'>
__init__
<__main__.st object at 0x000001BB998EFB88>
__init__
<__main__.st object at 0x000001BB998EFB88>
输出的流程
# st返回的是 <class '__main__.st'> 类的对象名
# print(st())时 st()创建了一个实例,python会为他创建一个实例,赋值给st() 然后再由print输出,因此会先执行__init__初始化,即先输出"__init__",再输出类的实例
# a = st() 创建一个实例a,会执行__init__ 后再输出 类的实例
# 输出类的实例内容包括 <__main__.st object at 0x00000141C826FE08> 创建实例的类的对象名 和 实例的存储地址
类的属性和方法
既然都说了处处皆对象,那么对象也可以是千变万化的,正如jack和helen也一定存在着不同的地方,这些他们所具有的相同或不同的内容我们称之为属性,为了方便我们将固有属性都放在类的中,使用__init__在构造函数的初始化阶段定义属性
class Student(object):
def __init__(self,name,age):
# 因人而异的属性
self.name = name
self.age = age
# 固有的属性
self.type = "People"
if __name__ == "__main__":
jack = Student("jack",12)
Helen = Student("Helen",18)
print(jack.name)
print(Helen.name)
# 输出固有属性
print(jack.type)
print(Helen.type)
# 随着年龄的增增长,属性也会随之变大
Helen.age = Helen.age + 1
jack.age +=1
print(jack.age)
print(Helen.age)
# Helen比较自私,他有别人没有的属性
Helen.HasCar = "Helen has one car "
print(Helen.HasCar)
输出内容
PS E:app> & C:/Users/alpaca/AppData/Local/Programs/Python/Python37/python.exe e:/app/demo2.py
jack
Helen
People
People
13
19
Helen has one car
记住,实例与实例之间的属性是相互独立的。
私有属性
Helen是一个女孩子,他很珍惜她的年龄,因此他不希望自己的年龄是会增大的,于是他想要将年龄这个属性据为己有,变成私有的属性。于是他在age的开头加上了双下划线__ 变成了__age,私有的(private),当你在外部想要去改变的时候,你会发现报错了。
class Student(object):
def __init__(self,name,age):
# 因人而异的属性
self.name = name
self.__age = age
if __name__ == "__main__":
jack = Student("jack",12)
Helen = Student("Helen",18)
print(jack.age)
jack.age +=1
print(jack.age)
输出的结果
PS E:app> & C:/Users/alpaca/AppData/Local/Programs/Python/Python37/python.exe e:/app/3.py
Traceback (most recent call last):
File "e:/app/3.py", line 17, in <module>
print(jack.age)
AttributeError: 'Student' object has no attribute 'age'
其实加了双下划线也不是一定不能访问,我们可以使用_Student__age去获取
当然年龄这些东西他是改变不了的,我们想办法去得到这些,于是我们要添加对应的方法
class Student(object):
def __init__(self,name,age):
# 因人而异的属性
self.name = name
self.__age = age
def get_age(self):
return self.__age
def __set_age(self,age):
self.__age = age
if __name__ == "__main__":
jack = Student("jack",12)
Helen = Student("Helen",18)
print(Helen.get_age())
Helen._Student__set_age(19)
print(Helen.get_age())
除了属性之外,类还可以包含方法,同样的我们通用(固有)的类的方法也会放进类中
class Student(object):
def __init__(self,name,age):
# 因人而异的属性
self.name = name
self.age = age
def say(self,msg):
print(msg+", my name is "+ self.name)
if __name__ == "__main__":
jack = Student("jack",12)
Helen = Student("Helen",18)
jack.say("hello")
输出内容为
PS E:app> & C:/Users/alpaca/AppData/Local/Programs/Python/Python37/python.exe e:/app/demo4.py
hello, my name is jack
类里面的所有方法都是以self为第一个参数,就指向创建的类本身,在调用时不需要传入任何的值
类的继承
在上面我们讲到过 所有的类都继承自object这个基础类。同样的,在类与类之间也可以互相继承,有了这个,在之前我们实现过的helen拥有汽车的事就不用遮遮掩掩的了,他完全可以自己再继承一个类
class Student(object):
def __init__(self,name,age):
# 因人而异的属性
self.name = name
self.age = age
# 固定的属性
self.type = "People"
class Man_HasCar(Student):
def HasCar(self):
print(self.name+" has one car ")
if __name__ == "__main__":
jack = Student("jack",12)
Helen = Man_HasCar("Helen","18")
Helen.HasCar()
获取对象的类型
type()
使用type()来判断对象类型
isinstance()
使用isinstance来判断某一个实例是否是某个类的实例,层级迭代也算,即假设B的实例是b,B的父类是A,print(instance(b,A)) = true
基本类型也可以用isinstance来判断 isinstance('a',str)
print(instance(b,A)) # true
isinstance('a',str) # True
isinstance([1, 2, 3], (list, tuple)) # True
dir()
如果要获取一个对象的所有属性和方法,可以使用dir函数,他返回一个包含字符串的list,比如获取一个str对象的方法
dir("ABC")
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
再这之中含双下划线的都是一些魔法方法,比如返回字符的长度,当然我们也可以使用len()去获取,但实际仍然就是调用了__len__这个方法
print("ABC".__len__())
print(len("ABC"))
class return_len_string(object):
# 重写__len__
def __len__(self):
return 100
rls = return_len_string()
print(len(rls))
# 返回100
获取对象属性
python准备了getattr()、setattr()、hasattr(),可以直接操作一个对象的属性
# 获得属性
getattr(cls,'name')
# 设置属性
setattr(cls,'name','Helen')
# 判断某个类是否具有某个属性
hasattr(cls,'name')
特殊的属性__slot__
我们知道由于Python是动态类的语言,再我们创建完一个类之后,并创建了他的实例,那么这个实例可以添加和绑定任何属性和方法,如果我们要限制实例的属性该怎么办,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
"""实际执行效果"""
>>> class Student(object):
... __slots__ = ('name', 'age')
...
>>> s = Student()
>>> s.name = 'digg'
>>> s.age = '19'
>>> s.score = 99
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
>>>
另外__slot__只对当前类有效,对继承类无效
__str__ ,__repr__
该魔法方法自定义返回内容
class tt(object):
def __str__(self):
return "自定义"
def __repr__(self):
return "自定义"
# 或者
__repr__ = __str__
class ss(object):
pass
if __name__ == "__main__":
print(tt())
print(ss())
__repr__其实和__str__差不多,大多数情况下使用str就差不多了
__iter__ 和 __next__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器
def __iter__(self):
return self # 实例本身即是迭代对象,故而返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环条件
raise StopIteration();
return self.a
# 测试
for n in Fib():
print(n)
枚举类
元类
staticmethod classmethod使用方法与区别
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
从它们的使用上来看,
- @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
- @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
- @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。
- 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
- 普通对象方法至少需要一个self参数,代表类对象实例
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 - 对于类方法,可以通过类来调用,就像Test.foo(),有点类似C++中的静态方法, 也可以通过类的一个实例来调用,就像Test().foo(),这里Test(),写成这样之后它就是类的一个实例了。
- 静态方法则没有,它基本上跟一个全局函数相同,一般来说用的很少
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数(当然,也可以用“self” 代替,个人认为是为了和类的self区分才用cls的),可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
例子
class cs(object):
def speak(self,name):
print("can you speak english? %s"%name)
@staticmethod
def say(name):
print("my name is %s "%(name))
@classmethod
def run(cls,name):
print("running now, %s"%(name))
if __name__ == "__main__":
cs.say("hzj")
cs.run("hzj")
聊一聊Python中的装饰器
https://blog.csdn.net/zhouchen1998/article/details/82933893
装饰器就是把几个函数共同的功能放在一起
Python 拥有丰富强大的功能和表达特性,其中之一便是装饰器,装饰器能够在不改变函数、方法、类本身的情况下丰富他们的功能。
应用场景:比如QQ会员吧,对于普通的会员我们使用的是func,但是对于VIP会员则是func_vip的方法,但是func与func_vip大部分功能的重叠性很高,没有必要重写一个func_vip方法,只需要丰富一下func就可以使用了,这时候我们就可以使用装饰器
前提
1.函数可以作为变量
def func(name):
print("my name is %s"%name)
func_other = func()
func_other("hzj")
2.将函数传递给函数
def func1(name):
return name
def func2(func):
print(func)
func2(func1("hzj"))
3.无参函数嵌套函数
def func_wrap():
def prt_func():
return 'hello,world'
return prt_func
hlowld = func_wrap()
print(hlowld())
4.有参函数嵌套函数
def func_wrap():
def prt_func(name):
return 'hello,'+name
return prt_func
hlo = func_wrap()
print(hlo('crossin'))
5.函数参数嵌套函数
def func(name):
return "hello %s"%(name)
def top_func(func):
def sec_func(name):
return "hello %s"%(func(name))
return sec_func
tt = top_func(func)
print(tt("hzj"))
讲一下装饰器
其实装饰器就是一种将函数当做参数的使用方法,具体如何使用我们来看一下,首先我们来看一下最普通的函数
# 最简单的函数
def general():
return "i am general func"
if __name__ == "__main__":
print(general())
# 带参数的最简单的参数
def general_params(name):
return "i am general %s"%name
if __name__ == "__main__":
print(general())
print(general_params("func"))
接下来是函数传递,在python中参数可以作为变量在函数与函数之间传递
# 函数作为变量
def bottom():
return "i ma bottom func"
b = bottom
print(b())
# -----------------
# 函数之间传递
# 函数作为变量传递
def bottom():
return "i am bottom func"
def top(func):
return "%s,ok!i know!"%(func())
t = top(bottom)
print(t)
接下来是函数的嵌套
# 不带参嵌套
def outer():
def inner():
return "ok!"
return inner
out = outer()
print(out())
# 带参嵌套
def outer(name):
def inner(name):
return "ok!%s"%name
return inner
out = outer("a")
print(out("b"))
# 结果ok!b
装饰器
# 首先定义一个普通的函数
def print_text(name):
return 'hello,'+ name
# 再定义一个嵌套函数,分别以函数和普通的字符串作为参数
def add_tag(func):
def prt_func(name):
return '<p>{0}</p>'.format(func(name))
return prt_func
# 将函数作为参数传递给 add_tag
hlo = add_tag(print_text)
# 将 'crossin' 作为参数传递给 hlo
print(hlo('crossin'))
# 结果 : <p>hello,crossin</p>
可以写成这样
def add_tag(func):
def prt_func(name):
return '<p>{0}</p>'.format(func(name))
return prt_func
@add_tag
def print_text(name):
return 'hello,'+ name
print_text('crossin')
来源
在Python这门语言中,存在着很多的魔法方法,除了这篇文章所介绍的__new__ init __call__以外还存在着其他的魔法方法
任何事物都有一个从创建,被使用,再到消亡的过程,在程序语言面向对象编程模型中,对象也有相似的命运:创建、初始化、使用、垃圾回收,不同的阶段由不同的方法(角色)负责执行。
定义一个类时,大家用得最多的就是 init 方法,而 new 和 call 使用得比较少,这篇文章试图帮助大家把这3个方法的正确使用方式和应用场景分别解释一下。
关于 Python 新式类和老式类在这篇文章不做过多讨论,因为老式类是 Python2 中的概念,现在基本没人再会去用老式类,老式类必须显示地继承 object,而 Python3 中,只有新式类,默认继承了 object,无需显示指定,本文代码都是基于 Python3 来讨论。
__init__方法
__init__方法负责对象的初始化,既然是面向对象的语言,存在对象,当然也存在对象的属性,而init就是用来初始化对象属性的模仿方法,时间为系统执行该方法前,其实该对象已经存在了,要不然初始化什么东西呢?先看例子
class A:
def __init__(self, *args, **kwargs):
print("__init__")
super(A,self).__init__()
def __new__(cls):
print("__new__")
print(cls)
return super(A,cls).__new__(cls)
if __name__ == "__main__":
A()
# -----------------
# result:
__new__
<class '__main__.A'>
__init__
也就是说__new__比__init__先执行,大致的执行流程为__new__接受一个cls,即当前类的并生成一个实例给init中的self,再由init初始化结构,另外__init__中除了self外,其他参数个数要与__new__相同
class B:
def __init__(self, *args, **kwargs):
print("init", args, kwargs)
def __new__(cls, *args, **kwargs):
print("new", args, kwargs)
print(cls)
self = super().__new__(cls)
print(self)
return super().__new__(cls)
if __name__=="main":
B()
__new__方法
其实在上面已经大致的能够知道__new__的用处了,即在初始化机构之前,返回一个类的对象实例给__init__.
一般我们不会去重写该方法,除非你确切知道怎么做,什么时候你会去关心它呢,它作为构造函数用于创建对象,是一个工厂函数,专用于生产实例对象。著名的设计模式之一,单例模式,就可以通过此方法来实现。
单例模式,在当前博客的图书馆一栏,设计模式中有讲到
__call__方法
__call__其实与callback回调函数有关,也就是可回调对象,说简单一点,该魔法方法是在当前类被调用时触发的
class C:
def __init__(self):
print("__init__")
def __call__(self):
print("__call__")
if __name__=="main":
c = C()
c()
# -----------
# result:
__init__
__call__