基础

init函数,开头和末尾各有两个下划线,这是一种约定,表示Python的默认函数,旨在避免Python默认方法与普通方法发生名称冲突。
类名应该采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线,实例名和模块名都采用小写格式,并在单词之间加上下划线。
在import时,先导入系统的库,然后空一行,再导入自己写的模块,这样更清晰。

  1. # object表示从object类继承,相当于Java中extends后的类名
  2. # object是否可省略与python版本有关,Python2.7需要,Python3可以不要
  3. # 为了兼容,建议还是不要省了
  4. class Student(object):
  5. age = 50; # 类变量,类似于Java中的static变量
  6. def __init__(self, name, score): # self 是一个指向实例的引用,类似于Java中的this
  7. # __init__ 是构造函数,每次初始化实例时,都会执行
  8. self.__name = name # python中对成员变量的引用需要使用self明确指出
  9. self.score = score # 因为这是跟实例有关的,所以需要self.
  10. def print_score(self):
  11. print('%s: %s' % (self.__name, self.score))
  12. # 可以创建多个实例 st1 = Student("zhangsan", 20) st2 = Student("lisi", 22)
  13. >>> bart = Student('Bart Simpson', 59) # 创建一个实例
  14. >>> bart.print_score()
  15. Bart Simpson: 59
  16. >>> bart.score = 60 # 可以通过实例名.成员变量来直接修改实例的成员变量值
  17. # 但__name变量就不行,因为在变量前加上__,表示这是私有变量,只能在类内部访问
  18. # 此时如果依然用bart.__name="james",并没有改变原来的成员变量,
  19. # 只是生成了一个新变量,因为__name被改名成了_Student__name,
  20. # 所以,实际上依然是可以在外部访问的,但是建议不要这么做。
  21. # 还有__name__这样的,不是私有变量,而是特殊变量
  22. # 还有_name,表示我不希望被外部引用,但是实际上就是普通成员变量

继承

普通继承

  1. class Car(object):
  2. def __init__(self, make, model, year):
  3. self.make = make
  4. self.model = model
  5. self.year = year
  6. self.odometer_reading = 0
  7. def get_descriptive_name(self):
  8. long_name = str(self.year) + ' ' + self.make + ' ' + self.model
  9. return long_name.title() # title()是首字母大写
  10. def fill_gas_tank(self):
  11. print("now fill car gas tank")
  12. class ElectricCar(Car):
  13. def __init__(self, make, model, year, battery_size):
  14. # Python2.7里super()的参数不能省
  15. super(ElectricCar, self).__init__(make, model, year)
  16. self.battery_size = battery_size
  17. def desc_battery(self):
  18. print("this car bettery size is :", self.battery_size)
  19. def fill_gas_tank(self): # 重写父类方法
  20. print("electric car doesn't need a gas tank!")
  21. my_car = Car('benz', 'outback', 2013)
  22. print(my_car.get_descriptive_name())
  23. my_tesla = ElectricCar('tesla', 'model_s', 2016, 70)
  24. print(my_tesla.get_descriptive_name())
  25. my_tesla.fill_gas_tank()
  26. # 2013 Benz Outback
  27. # 2016 Tesla Model_S
  28. # electric car doesn't need a gas tank!

继承时的参数传递

当没有明确写出构造函数init时,系统会默认传参给父类。

  1. import collections
  2. class Student1(collections.namedtuple('Person1', ["name", "age"])):
  3. def print(self):
  4. print("this is print function")
  5. s1 = Student1("wangwu", 30)
  6. print("s1 =", s1)
  7. ----------------------------------------------------------------------------
  8. class Person2():
  9. def __init__(self, name, age):
  10. print("this is Person2 init func")
  11. print("name =", name)
  12. print("age = ", age)
  13. class Student2(Person2):
  14. pass
  15. s2 = Student2("sunliu", 40)
  16. print("s2 =", s2)
  17. ----------------------------------------------------------------------------
  18. class Person3():
  19. pass
  20. class Student3(Person3, collections.namedtuple('Person1', ["name", "age"])):
  21. def print(self):
  22. print("this is print function")
  23. print("name =", self.name)
  24. s3 = Student3("zhaoqi", 70)
  25. print("s3 =", s3)
  26. s3.print()
  27. ----------------------------------------------------------------------------
  28. # res
  29. s1 = Student1(name='wangwu', age=30)
  30. this is Person2 init func
  31. name = sunliu
  32. age = 40
  33. s2 = <__main__.Student2 object at 0x7ffdacaf0668>
  34. s3 = Student3(name='zhaoqi', age=70)
  35. this is print function
  36. name = zhaoqi

多继承

python中的多继承,在初始化时是按照MRO(Method Resolution Order)表来进行顺序的解析。

  1. class Base(object):
  2. def __init__(self):
  3. print("enter Base")
  4. print("leave Base")
  5. class A(Base):
  6. def __init__(self):
  7. print("enter A")
  8. super(A, self).__init__()
  9. print("leave A")
  10. class B(Base):
  11. def __init__(self):
  12. print("enter B")
  13. super(B, self).__init__()
  14. print("leave B")
  15. class C(A, B):
  16. def __init__(self):
  17. print("enter C")
  18. super(C, self).__init__()
  19. print("leave C")
  20. c = C()
  21. print(C.mro())
  22. ---------------------------------------
  23. # res
  24. enter C
  25. enter A
  26. enter B
  27. enter Base
  28. leave Base
  29. leave B
  30. leave A
  31. leave C
  32. [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
  33. 从结果可以看出按照MRO表,就是先进入A,然后Asuper(A, self),
  34. 注意,此时的self其实是C对象,所以,按照C对象的顺序表,就进入了B
  35. Bsuper(B, self),此时self还是C对象,于是按照顺序表进入了Base,因此会有以下结果。

装饰器

@classmethod

classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

  1. class A(object):
  2. b = 2
  3. def __init__(self, a=1):
  4. print("this is init method,a=", a)
  5. self.a = a
  6. def func1(self):
  7. print("fun1")
  8. @classmethod
  9. def func3(cls):
  10. print("func3")
  11. @classmethod
  12. def func2(cls):
  13. print("func2")
  14. cls().func1() # func1是属于实例的方法,因此就需要先实例化,再调用
  15. cls.func3() # func3是属于类的方法,可以直接用类调用
  16. cls(5) # 实例化一个对象
  17. print("a=", cls(6).a)
  18. print("b=", cls.b)
  19. A.func2()
  20. -- output
  21. func2
  22. this is init method,a= 1
  23. func1
  24. func3
  25. this is init method,a= 5
  26. this is init method,a= 6
  27. a= 6
  28. b= 2

@staticmethod

staticmethod不需要任何参数,与classmethod不同,classmethod的主要使用场景是当你想用类来调用方法,而不是对象来调用方法时,而staticmethod一般是用于多个类之间共享该方法,就可以定义为staticmethod,不过在python中,这种场景很少。

@property

@property,把一个getter方法变成属性,同时会创建另外一个装饰器,如下代码就是@width.setter@height.setter
从resolution可能看的更清晰一点,创建一个resolution方法,在通过装饰器以后,就默认Screen类有了一个属性,叫做resolution,所以下面可以通过s.resolution调用。并且此时resolution是一个只读属性,不能被修改,从而达到访问控制的目的。

  1. class Screen(object):
  2. @property
  3. def width(self):
  4. return self._width
  5. @width.setter
  6. def width(self, width):
  7. self._width = width
  8. @property
  9. def height(self):
  10. return self._height
  11. @height.setter
  12. def height(self, height):
  13. self._height = height
  14. @property
  15. def resolution(self):
  16. return self._width * self._height
  17. # 测试:
  18. s = Screen()
  19. s.width = 1024
  20. s.height = 768
  21. print('resolution =', s.resolution)
  22. if s.resolution == 786432:
  23. print('测试通过!')
  24. else:
  25. print('测试失败!')
  26. s1 = Screen(100, 700)
  27. print('resolution =', s1.resolution)
  28. # res
  29. resolution = 786432
  30. 测试通过!
  31. Traceback (most recent call last):
  32. File "/Users/liufei/Downloads/test.py", line 30, in <module>
  33. s1 = Screen(100, 700)
  34. TypeError: object() takes no parameters

一些奇技淫巧

collections.namedtuple

第一次看到这个类是从tensorflow的源码中,tf.featurecolumn.numericcolumn中的_NumericColumn类继承自collections.namedtuple,这样_NumericColumn可以在没有__init方法的情况下,直接使用self.key等属性:

  1. class _NumericColumn(_DenseColumn,
  2. collections.namedtuple('_NumericColumn', [
  3. 'key', 'shape', 'default_value', 'dtype',
  4. 'normalizer_fn'
  5. ])):
  6. """see `numeric_column`."""
  7. @property
  8. def name(self):
  9. return self.key

collections.namedtuple是一个工厂方法,它可以动态的创建一个继承tuple的子类。跟tuple相比,返回的子类可以使用名称来访问元素。最大的好处应该是可以提高代码的可读性,最常见的是作为父类给子类提供属性,可以减少很多代码,并且子类的返回结果也更清晰易懂,可以参考上面#继承时的参数传递部分,可以看到,如果是从namedtuple继承,子类返回结果直接可以看到类名,属性值等,而从普通类继承的,则只返回子类的地址。

  1. import collections
  2. Student = collections.namedtuple('Teacher', ['name', 'age'])
  3. s = Student('zhangsan', 25)
  4. print(s)
  5. print("name =", s.name)
  6. t = Teacher("lisi", 26)
  7. # res
  8. Teacher(name='zhangsan', age=25)
  9. name = zhangsan
  10. Traceback (most recent call last):
  11. File "/Users/liufei/Downloads/test.py", line 13, in <module>
  12. t = Teacher("lisi", 26)
  13. NameError: name 'Teacher' is not defined

理解dict

dict用来存储类和对象的属性和方法,以及其他的一些信息。
类的dict存储自己类层面的信息,如类的变量,函数等,不存储函数内的信息,同时不包括父类的信息,对象的dict只存储自己独有的信息。

  1. # -*- coding: utf-8 -*-
  2. class A(object):
  3. """
  4. This is doc.
  5. """
  6. a = 0
  7. b = 1
  8. def __init__(self):
  9. self.a = 2
  10. def test(self):
  11. print 'A func.'
  12. @staticmethod
  13. def static_test(self):
  14. print 'A static func.'
  15. @classmethod
  16. def class_test(self):
  17. print 'A calss func.'
  18. obj = A()
  19. print("A.__dict__:", A.__dict__)
  20. print("\n")
  21. print("obj.__dict__:", obj.__dict__)
  22. class B(A):
  23. def __init__(self, c=3):
  24. super(A, self).__init__()
  25. self.c = c
  26. def test(self):
  27. print 'B func.'
  28. print("B.__dict__:", B.__dict__)
  29. print("ojb_B.__dict:", B().__dict__)
  30. --output
  31. ('A.__dict__:', dict_proxy({'a': 0, '__module__': '__main__', 'b': 1,
  32. 'class_test': <classmethod object at 0x10ade7210>,
  33. '__dict__': <attribute '__dict__' of 'A' objects>,
  34. '__init__': <function __init__ at 0x10ad70050>,
  35. 'test': <function test at 0x10ad702a8>,
  36. '__weakref__': <attribute '__weakref__' of 'A' objects>,
  37. '__doc__': '\n This is doc.\n ',
  38. 'static_test': <staticmethod object at 0x10ade71d8>}))
  39. ('obj.__dict__:', {'a': 2})
  40. ('B.__dict__:', dict_proxy({'test': <function test at 0x10ae670c8>,
  41. '__module__': '__main__',
  42. '__doc__': None,
  43. '__init__': <function __init__ at 0x10ae67050>}))
  44. ('ojb_B.__dict:', {'c': 3})