基础
像init函数,开头和末尾各有两个下划线,这是一种约定,表示Python的默认函数,旨在避免Python默认方法与普通方法发生名称冲突。
类名应该采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线,实例名和模块名都采用小写格式,并在单词之间加上下划线。
在import时,先导入系统的库,然后空一行,再导入自己写的模块,这样更清晰。
# object表示从object类继承,相当于Java中extends后的类名# object是否可省略与python版本有关,Python2.7需要,Python3可以不要# 为了兼容,建议还是不要省了class Student(object):age = 50; # 类变量,类似于Java中的static变量def __init__(self, name, score): # self 是一个指向实例的引用,类似于Java中的this# __init__ 是构造函数,每次初始化实例时,都会执行self.__name = name # python中对成员变量的引用需要使用self明确指出self.score = score # 因为这是跟实例有关的,所以需要self.def print_score(self):print('%s: %s' % (self.__name, self.score))# 可以创建多个实例 st1 = Student("zhangsan", 20) st2 = Student("lisi", 22)>>> bart = Student('Bart Simpson', 59) # 创建一个实例>>> bart.print_score()Bart Simpson: 59>>> bart.score = 60 # 可以通过实例名.成员变量来直接修改实例的成员变量值# 但__name变量就不行,因为在变量前加上__,表示这是私有变量,只能在类内部访问# 此时如果依然用bart.__name="james",并没有改变原来的成员变量,# 只是生成了一个新变量,因为__name被改名成了_Student__name,# 所以,实际上依然是可以在外部访问的,但是建议不要这么做。# 还有__name__这样的,不是私有变量,而是特殊变量# 还有_name,表示我不希望被外部引用,但是实际上就是普通成员变量
继承
普通继承
class Car(object):def __init__(self, make, model, year):self.make = makeself.model = modelself.year = yearself.odometer_reading = 0def get_descriptive_name(self):long_name = str(self.year) + ' ' + self.make + ' ' + self.modelreturn long_name.title() # title()是首字母大写def fill_gas_tank(self):print("now fill car gas tank")class ElectricCar(Car):def __init__(self, make, model, year, battery_size):# Python2.7里super()的参数不能省super(ElectricCar, self).__init__(make, model, year)self.battery_size = battery_sizedef desc_battery(self):print("this car bettery size is :", self.battery_size)def fill_gas_tank(self): # 重写父类方法print("electric car doesn't need a gas tank!")my_car = Car('benz', 'outback', 2013)print(my_car.get_descriptive_name())my_tesla = ElectricCar('tesla', 'model_s', 2016, 70)print(my_tesla.get_descriptive_name())my_tesla.fill_gas_tank()# 2013 Benz Outback# 2016 Tesla Model_S# electric car doesn't need a gas tank!
继承时的参数传递
当没有明确写出构造函数init时,系统会默认传参给父类。
import collectionsclass Student1(collections.namedtuple('Person1', ["name", "age"])):def print(self):print("this is print function")s1 = Student1("wangwu", 30)print("s1 =", s1)----------------------------------------------------------------------------class Person2():def __init__(self, name, age):print("this is Person2 init func")print("name =", name)print("age = ", age)class Student2(Person2):passs2 = Student2("sunliu", 40)print("s2 =", s2)----------------------------------------------------------------------------class Person3():passclass Student3(Person3, collections.namedtuple('Person1', ["name", "age"])):def print(self):print("this is print function")print("name =", self.name)s3 = Student3("zhaoqi", 70)print("s3 =", s3)s3.print()----------------------------------------------------------------------------# ress1 = Student1(name='wangwu', age=30)this is Person2 init funcname = sunliuage = 40s2 = <__main__.Student2 object at 0x7ffdacaf0668>s3 = Student3(name='zhaoqi', age=70)this is print functionname = zhaoqi
多继承
python中的多继承,在初始化时是按照MRO(Method Resolution Order)表来进行顺序的解析。
class Base(object):def __init__(self):print("enter Base")print("leave Base")class A(Base):def __init__(self):print("enter A")super(A, self).__init__()print("leave A")class B(Base):def __init__(self):print("enter B")super(B, self).__init__()print("leave B")class C(A, B):def __init__(self):print("enter C")super(C, self).__init__()print("leave C")c = C()print(C.mro())---------------------------------------# resenter Center Aenter Benter Baseleave Baseleave Bleave Aleave C[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]从结果可以看出按照MRO表,就是先进入A,然后A再super(A, self),注意,此时的self其实是C对象,所以,按照C对象的顺序表,就进入了B,B再super(B, self),此时self还是C对象,于是按照顺序表进入了Base,因此会有以下结果。
装饰器
@classmethod
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
class A(object):b = 2def __init__(self, a=1):print("this is init method,a=", a)self.a = adef func1(self):print("fun1")@classmethoddef func3(cls):print("func3")@classmethoddef func2(cls):print("func2")cls().func1() # func1是属于实例的方法,因此就需要先实例化,再调用cls.func3() # func3是属于类的方法,可以直接用类调用cls(5) # 实例化一个对象print("a=", cls(6).a)print("b=", cls.b)A.func2()-- outputfunc2this is init method,a= 1func1func3this is init method,a= 5this is init method,a= 6a= 6b= 2
@staticmethod
staticmethod不需要任何参数,与classmethod不同,classmethod的主要使用场景是当你想用类来调用方法,而不是对象来调用方法时,而staticmethod一般是用于多个类之间共享该方法,就可以定义为staticmethod,不过在python中,这种场景很少。
@property
@property,把一个getter方法变成属性,同时会创建另外一个装饰器,如下代码就是@width.setter和@height.setter。
从resolution可能看的更清晰一点,创建一个resolution方法,在通过装饰器以后,就默认Screen类有了一个属性,叫做resolution,所以下面可以通过s.resolution调用。并且此时resolution是一个只读属性,不能被修改,从而达到访问控制的目的。
class Screen(object):@propertydef width(self):return self._width@width.setterdef width(self, width):self._width = width@propertydef height(self):return self._height@height.setterdef height(self, height):self._height = height@propertydef resolution(self):return self._width * self._height# 测试:s = Screen()s.width = 1024s.height = 768print('resolution =', s.resolution)if s.resolution == 786432:print('测试通过!')else:print('测试失败!')s1 = Screen(100, 700)print('resolution =', s1.resolution)# resresolution = 786432测试通过!Traceback (most recent call last):File "/Users/liufei/Downloads/test.py", line 30, in <module>s1 = Screen(100, 700)TypeError: object() takes no parameters
一些奇技淫巧
collections.namedtuple
第一次看到这个类是从tensorflow的源码中,tf.featurecolumn.numericcolumn中的_NumericColumn类继承自collections.namedtuple,这样_NumericColumn可以在没有__init方法的情况下,直接使用self.key等属性:
class _NumericColumn(_DenseColumn,collections.namedtuple('_NumericColumn', ['key', 'shape', 'default_value', 'dtype','normalizer_fn'])):"""see `numeric_column`."""@propertydef name(self):return self.key
collections.namedtuple是一个工厂方法,它可以动态的创建一个继承tuple的子类。跟tuple相比,返回的子类可以使用名称来访问元素。最大的好处应该是可以提高代码的可读性,最常见的是作为父类给子类提供属性,可以减少很多代码,并且子类的返回结果也更清晰易懂,可以参考上面#继承时的参数传递部分,可以看到,如果是从namedtuple继承,子类返回结果直接可以看到类名,属性值等,而从普通类继承的,则只返回子类的地址。
import collectionsStudent = collections.namedtuple('Teacher', ['name', 'age'])s = Student('zhangsan', 25)print(s)print("name =", s.name)t = Teacher("lisi", 26)# resTeacher(name='zhangsan', age=25)name = zhangsanTraceback (most recent call last):File "/Users/liufei/Downloads/test.py", line 13, in <module>t = Teacher("lisi", 26)NameError: name 'Teacher' is not defined
理解dict
dict用来存储类和对象的属性和方法,以及其他的一些信息。
类的dict存储自己类层面的信息,如类的变量,函数等,不存储函数内的信息,同时不包括父类的信息,对象的dict只存储自己独有的信息。
# -*- coding: utf-8 -*-class A(object):"""This is doc."""a = 0b = 1def __init__(self):self.a = 2def test(self):print 'A func.'@staticmethoddef static_test(self):print 'A static func.'@classmethoddef class_test(self):print 'A calss func.'obj = A()print("A.__dict__:", A.__dict__)print("\n")print("obj.__dict__:", obj.__dict__)class B(A):def __init__(self, c=3):super(A, self).__init__()self.c = cdef test(self):print 'B func.'print("B.__dict__:", B.__dict__)print("ojb_B.__dict:", B().__dict__)--output('A.__dict__:', dict_proxy({'a': 0, '__module__': '__main__', 'b': 1,'class_test': <classmethod object at 0x10ade7210>,'__dict__': <attribute '__dict__' of 'A' objects>,'__init__': <function __init__ at 0x10ad70050>,'test': <function test at 0x10ad702a8>,'__weakref__': <attribute '__weakref__' of 'A' objects>,'__doc__': '\n This is doc.\n ','static_test': <staticmethod object at 0x10ade71d8>}))('obj.__dict__:', {'a': 2})('B.__dict__:', dict_proxy({'test': <function test at 0x10ae670c8>,'__module__': '__main__','__doc__': None,'__init__': <function __init__ at 0x10ae67050>}))('ojb_B.__dict:', {'c': 3})
