OOP Final
Destructor in C++
• Called when an object is deleted (with delete) • Has the same name as the constructor with '~' added to the beginning • Responsible for cleaning up any dynamically-allocated instance variables Ex. class Example { string* ptr; ~Example() { delete ptr; } };
True or False: State transitions can be handled in either the context class or the state classes.
True
True or False? A command object can defer execution of some action.
True
True or False? An observer can be removed at runtime.
True
True or False? Decorators, Adapters, and Facades are all types of Wrappers.
True
True or False? The iterator pattern is built into Python's for loops.
True
True or False? The yield keyword causes a function to return a Generator object.
True
True or False? super is not a built in keyword in C++.
True
True or False? The code below could be a valid method signature. const int* const foo(const int* const bar) const;
True This is a const method that takes a const pointer to a const int and returns a const pointer to a const int
In the following code, what method must be defined on foo? [x for x in foo]
__iter__
Statically-defined decorators in Python
• Can decorate either functions or methods (remember, functions are objects) • Use @decorator_name syntax before the function/method name • Less powerful: baked into code, cannot decorate an object at runtime • Often easier to read Ex. @abstractmethod def mymethod(self): ... @classmethod def myclassmethod(cls): ...
Composition versus Inheritance
• Composition is used as a catch-all for "has-a" relationship; is more flexible than inheritance; can swap out references to objects • Inheritance is more permanent; can never swap what its parent class is
When should one use the adapter pattern?
• Converting arguments to a different format • Rearranging the order of arguments • Calling a differently named method • The adapter pattern does not provide new behavior to an object, unlike the decorator pattern
State pattern
• Behavioral pattern • Similar to strategy pattern • Used in problems that make sense as state machines • The behavior of an object changes based on its state • Transitions may be defined in a manager class or state classes themselves
Template method pattern
• Behavioral pattern • Used to factor out duplicate code • Re-used code is defined and called in base class • Subclasses override only certain steps in a process • Template pattern is a generalization of the strategy pattern (multiple steps instead of a single method) Ex. class QueryTemplate: def connect(self): self.conn = sqlite3.connect("sales.db") ' def construct_query(self): raise NotImplementedError() def do_query(self): results = self.conn.execute(self.query) self.results = results.fetchall() def format_results(self): output = [] for row in self.results: row = [str(i) for i in row] output.append(", ".join(row)) self.formatted_results = "\n".join(output) def output_results(self): raise NotImplementedError() def process_format(self): self.connect() self.construct_query() self.do_query() self.format_results() self.output_results() class NewVehiclesQuery(QueryTemplate): def construct_query(self): self.query = "select * from Sales where new='true'" def output_results(self): print(self.formatted_results)
"Super" in C++
• C++ has no super keyword • Can access the parent class' methods using the class name; this is only necessary if there is a name collision Ex. class Mother { public: int myint; Mother(int a): myint(a) {} } class Son: public Mother { // no naming conflict Son(int a ): Mother(a) {} } class Mother { public: virtual void print() {return;} } class Son: public Mother { public: void print() { cout << "foo"; // name conflict need the scope resolution operator Mother::print(); } }
Decorator UML
• Core and decorator objects implement the same interface • Decorator objects hold a reference to another interface object (either the core or another decorator) • Can also have an ABC which defines the interface of concrete decorator classes
Singleton pattern
• Creational pattern • Make sure a class only ever has one instance • Lazy initialization: delaying the creation of an object, the calculation of value, or some other expensive process until the first time it is needed • Global access: instance is accessed through the class name • Use the Python constructor __new__ to ensure only one instance is ever created Ex. class OneOnly: _singleton = None def __new__(cls, *args, **kwargs): if not cls._singleton: # Notice that we pass the class-not an object instance-to super cls._singleton = super(OneOnly, cls ).__new__(cls, *args, **kwargs) return cls._singleton
Iterator UML
• Each ConcreteIterator inherits from the Iterator class, which defines the interface for Iterator objects • Each iterator typically defines the has_more and next methods • Each iterable object has a createIterator method that returns an iterator for that object
Instantiating objects in C++
Allocating memory on the stack: Person tim; Person tim("Tim"); Allocating memory on the heap: • new is a keyword in C++ that first allocates memory for an object on the heap and then calls its constructor • We can also call delete on an object pointer • delete first calls that object's destructor and thereafter frees its memory from the heap Ex. Person* tim = new Person("Tim"); delete tim;
What are the four categories of design patterns?
(1) Creational Ways to manage the creation of objects (2) Structural Organizing classes and objects to provide new functionality (3) Behavioral Manage communication between objects (4) Concurrency Address challenges in multi-threading/multi-processing
What will the above code print? #include <iostream> using namespace std; int main () { int a = 5; int* b = &a; int*& c = b; (*c) = 10; cout << a << endl; return 0; }
10 Since c is a reference to an int*, then 'c' is an int*; thereafter, we see that '*c' is an int.
Which of these code segments is correct? (1) class Dummy { int x; Dummy(int x){ this.x = x; } }; (2) class Dummy { int x; Dummy(int x){ this->x = x; } };
2 'this' is a pointer to the object instance
What does this code print? lst = [1,2,3,4,5] it1 = iter(lst) it2 = iter(lst) next(it2) next(it1) next(it1) next(it2) next(it1) print(next(it1))
4 The iterator iterates to the fourth element (index 3)
In Python, how do you know when an iterator has finished iterating? A. calling __next__ raises a StopIteration exception B. calling __next__ returns None C. calling __next__ returns False D. calling __done__ returns True
A
What is the difference between these two lines of code? (1) Person* tim = new Person("Tim"); (2) Person* tim = (Person*)malloc(sizeof(Person)); A. The second one does not call the constructor B. The second one will not compile in either C or C++ C. The second one allocates memory on the heap while the first one uses the stack D. The second one will only compile in C, but not C++
A new calls both malloc as well as the object's constructor
In the State pattern which of these could be converted to singletons? A. User B. States C. Context D. Parser
B
When is the initializer list preferred over initializing in the constructor body? A. When the constructor is not overloaded B. When initializing an instance variable that is another object C. When there are many instance variables to set up
B If initializing an instance variable that is another object in the constructor body, then that object's constructor will be called twice
In the state pattern, the states are represented by... A. a string attribute in the context object B. state objects C. a transition table
B In the state pattern, each individual state is represented as a class inheriting from an ABC.
In which of the following cases would a Singleton be most problematic? A. configuration object B. logging object C. objects with mutable state D. objects with no state
C
A decorator must: A. hold a reference to the original subject B. not be used more than once on the same object C. maintain the same public interface as the original subject D. be defined using the @decorator syntax
C A: A decorator holds a reference to either the original subject or another decorator B: Decorator can be used any number of times D: Decorators can be either static or dynamic
Which of the following is not an advantage of decorators over subclasses? A. Decorators can be layered in different combinations B. New functionality can be added to an object at runtime C. Decorators do not involve writing new classes
C One must define new concrete decorator classes
Python getters and setters
Can define a getter and setter together using a property property(fget=None, fset=None, fdel=None, doc=None) class Person: def __init__(self, name): self._name = name def get_name(self): print('Getting name') return self._name def set_name(self, value): print('Setting name to ' + value) self._name = value def name = property(get_name, set_name) Or can define getters and setters separately using property decorators: @property def age(self): print("getter method called") return self._age @age.setter def age(self, a): if(a < 18): raise ValueError("Sorry you age is below eligibility criteria") print("setter method called") self._age = a mark = Geeks() # calls age setter mark.age = 19 # calls age getter print(mark.age)
In the auction example, the auctioneer is... A. an observer B. a decorator C. a strategy D. the subject
D
What is the difference between structs and classes? A. structs cannot inherit from other structs B. structs cannot have methods C. structs are stored on the stack and classes are stored on the heap D. default visibility is public for structs and private for classes
D
Which of the following operators cannot be overloaded? A. [] B. + C. new D. sizeof E. -> F. == G. =
D
Although the purpose and usage of these two patterns differs, which of design pattern is structurally most similar to the composite pattern based on the UML diagrams?
Decorator pattern
True or False? A subject can have only one observer attached to it.
False
True or False? The adapter pattern cannot be used to change the number of arguments to a method.
False
True or False? Const methods cannot be used by non-const objects.
False All objects (const and non-const) can call const methods
True or False? The default destructor will delete all objects pointed to by instance variables.
False One should define their own destructor to free memory that is dynamically-allocated
True or False? Like a constructor, a class cannot define more than one factory method.
False This is the abstract factory pattern (has more than one factory method)
True or False? In the XmlParser example we needed to add the singleton code to each state class separately so that each would have its own singleton instance.
False • We define the class method get_instance in the abstract base class, which returns the singleton instance • Since this method is then inherited by each of the state classes, calling get_instance on each state subclass returns the singleton as an instance of the subclass • This same behavior can be achieved by using the constructor __new__ **This is a very important example, review it
What does this line return? (x for x in [1,2,3,4])
Generator
In Python, all objects are instantiated on the ______.
Heap The stack stores variable references to those objects
Statically versus dynamically-typed languages
In statically-typed languages, the type of an object is determined at compile-time; in dynamically-typed languages, types are checked at runtime • C++ is a statically-typed language, Python is a dynamically-typed language
Differences between Python and C++
Memory Management: C++ has manual memory management, Python has automatic garbage collection Constants: C++ has constants (variables, classes, methods), Python does not Privacy: C++ has public, protected, private attributes and methods, Python does not Running Programs: C++ files are compiled to platform-specific binary code, Python code is run as input to an interpreter (compiled versus interpreted languages)
What code replaces XXXX in this example? def log_calls(func): def wrapper(*args, **kwargs): now = time.time() print("Calling {0} with {1} and {2}".format( func.__name__, args, kwargs)) return_value = XXXX print( "Executed {0} in {1}ms".format( func.__name__, time.time() - now)) return return_value return wrapper Which of the following are ways to decorate a function foo() with the log_calls decorator above? A. @log_calls def foo(): B. def foo(log_calls): C. log_calls(foo()) D. foo = log_calls(foo) E. @log_calls(foo)
func(*args, **kwargs) and A, D • This code is tricky to understand • log_calls(foo) returns a function which calls foo with any arguments passed in (*args, **kwargs)
Passing references in C++
int x = 3; C-style: void myfunction1(int* a) { (*a) *= 2; } // pass the address of int x myfunction1(&x); C++ style: void myfunction1(int& a) { a *= 2; } // pass x directly (do not need to get its address first) myfunction1(x);
The ______ state is shared in the flyweight object, the ______ state is not.
intrinsic, extrinsic
C++ visibility: public, protected, private
public: • accessible to everyone protected: • accessible to subclasses • accessible to friends private: • default for class • only accessible from objects of this class i.e. not inherited by subclasses Ex. class Circle { double radius; }; int main() { Circle c; c.radius = 5; } ERROR: In function 'int main()': 11:16: error: 'double Circle::radius' is private double radius; ^ 31:9: error: within this context obj.radius = 5;
C++ Structs
• A contiguous chunk of memory • Unlike arrays, fields can have different types • Unlike classes, all fields are public • Defined using the keyword struct • Fields are accessed using the . operator Ex. struct product { int weight; double price; double calcCost(){ return weight*price;} }; struct product apple = {1, 0.95}; struct product* ptr = &apple; OR struct product apple ; apple.weight = 1; apple.price = 0.95;
Factory method
• A creational pattern • A creator class defines a factory method that returns a new product object (method can be abstract, so subclasses must implement their own method, or return a default product) • Concrete creators override the base factory method so it returns a different type (i.e. subclass) of product • The product class declares the interface shared by all products; all concrete product classes must have this same interface (via inheritance), but can have different implementations Ex. class PlayerFactory(metaclass=ABCMeta): @abstractmethod def create_player(self): return class HumanPlayerFactory(PlayerFactory): def create_player(self): return HumanPlayer()
C++ 'this' keyword
• A pointer to the current instance of the class within a method call • Similar to 'self' in Python • Not required to access instance variables/methods Ex. class Dummy { int a; public: bool isitme(Dummy& param); Dummy(int myint) : a(myint){} printint() { printf("%d", this->a); } }; Dummy::isitme(Dummy& param) { // this is a Dummy pointer if ¶m == this { return True; } else { return False; } }
Overloading operators in C++
• A similarity between Python and C++ • Classes can redefine operators like =, *, + • Need to define a public method called 'operator[operator]' or 'operator [operator]' Ex. operator+ or operator +, operator<< or operator << • 'operator<<' is similar to __str__ Ex. Person p; stdout << p; Ex. CVector CVector::operator+ (const CVector& param) { CVector tmp; tmp.x = x + param.x; tmp.y = y + param.y; return tmp; }
Decorator pattern
• A structural pattern • Adds capabilities to an object dynamically (without monkey patching; an alternative to the decorator) • Wraps an object with another object; wrap objects recursively • Core interface remains untouched Ex. class Component(metaclass=abc.ABCMeta): @abc.abstractmethod def operation(self): pass class Decorator(Component, metaclass=abc.ABCMeta): def __init__(self, component): self._component = component @abc.abstractmethod def operation(self): pass class ConcreteDecoratorA(Decorator): def operation(self): print("decorated by A") # calls the method of its component self._component.operation()
Facade pattern
• A structural pattern • Provides a simpler or higher-level interface to some code • A type of wrapper, like decorator and adapter • Does not provide any new functionality, like adapter Ex. for loops are a facade over iterators Ex. class VideoFile // ... class OggCompressionCodec // ... class MPEG4CompressionCodec // ... class CodecFactory // ... class BitrateReader // ... class AudioMixer // ... class VideoConverter is method convert(filename, format): File is file = new VideoFile(filename) sourceCodec = new CodecFactory.extract(file) if (format == "mp4") destinationCodec = new MPEG4CompressionCodec() else destinationCodec = new OggCompressionCodec() buffer = BitrateReader.read(filename, sourceCodec) result = BitrateReader.convert(buffer, destinationCodec) result = (new AudioMixer()).fix(result) return new File(result)
Initializer list
• A way of initializing the instance variables for a class • Particularly advantageous when a class has another object as one of its attributes; otherwise, the constructor for this object is called twice Ex. class Cylinder { Circle base; double height; public: Cylinder(double r, double h): base(r), height(h) {} };
Templates in C++
• Allows the type of a variable in a class to be determined during instantiation • Unnecessary in Python (Python is dynamically-typed) Ex. using namespace std; template <class T> class Mypair { T a, b; public: Mypair(T first, T second): a(first), b(second) {} T getmax(); }; Mypair <int> myobject(100, 75); Mypair <double> myobject(3.14, 2.72);
Iterator pattern
• Behavioral pattern • Created to traverse the elements from another object • Should traverse without exposing an object's internal structure • Should not need to change the original object's interface • In languages such as C, iteration is controlled externally: Ex. for (int i = 0; i < 5; i++) { print("%d", i); } • Using the iterator pattern, iteration is controlled internally: Ex. while not (iterator.done()): print(iterator.next())
Strategy pattern
• Behavioral pattern • Implement different solutions to a problem, each in a different object • Choose the appropriate solution at runtime • Object aggregates a strategy object • Each strategy object has only one method and no data • Can be simplified using first-class functions (still strategy pattern since functions are objects) Ex. class Context: def __init__(self, strategy): self._strategy = strategy def context_interface(self): self._strategy() class Strategy(metaclass=abc.ABCMeta): @abc.abstractmethod def __call__(self): pass class ConcreteStrategyA(Strategy): def __call__(self): print("A") or could do def strategy_a(): print("A")
Observer pattern
• Behavioral pattern • Observers are subscribed to updates (subject --> observer) on one or more other objects • Observer implements an update method • An object can have multiple observers • An observer can be added or removed at runtime • Used in event-driven programming Ex. In GUIs: Binding a handler (observer) to an event queue (subject) Ex. Auctioneer is the subject, bidders are the observers; auctioneer broadcasts new high bid to the bidders • Ideally, the subject does not need to know what the observer does, it just notifies the observer to update • Subject should not "push" data to the observer why? is possible, but would need to call observer.update(data); what data to send, would need to know the implementation of observer, which is bad for abstraction; if send too much data breaks the principle of encapsulation • Instead, the observer should pull what it needs; observer would need a reference to subject; only relevant data would need to be publicly accessible Other considerations: • If many small changes are made on the subject, should update be called each time? • An observer could watch multiple subjects, but would need to know from which subject update was called; also bad for memory management (when should an observer be deleted)
Command pattern
• Behavioral pattern • Represents an action as an object • Sender (or Invoker) class holds a reference to the command object • Sender is not responsible for creating the command object • Sender defines an executecommand method that calls execute on the command object • Command object handles the details of how a request is passed to a receiver • The receiver itself does the work of performing the command (i.e. the receiver manages its own internal state) • To create a command object, one must pass all request parameters (i.e. all arguments required to perform the action), including a receiver instance • The receiver is associated with a command • Object-oriented callback • Useful for deferring execution of an action • Can help with undo, redo functionality since the action can have a method to reverse itself
C++ Classes
• Functionally nearly identical to structs, but default visibility is private • Need to explicitly specify which methods, attributes are public, protected • Defined using the keyword class • Methods can be defined outside the class definition • Do not need to reference the object instance with 'self' • The :: operator is used when defining a method outside of a class and to access a class' static variables • Otherwise, attributes and methods are accessed using the . operator Ex. class Rectangle{ int width, height; public: void set_values(int, int); int area(){return width*height;} } ; void Rectangle::set_values(int x, int y) { width = x; height = y; }
What are design patterns?
• General solutions to commonly-occurring problems • Not finished code, but rather a template
How are iterators built into Python, C++?
• In Python, all for-loops use the iterator pattern: Ex. for i in range(n): print(i) •In C++, incrementing pointers steps through arrays (considers the size of objects) Ex. ptr++ *ptr
Polymorphism in C++
• In order to achieve runtime polymorphism, one must implement a virtual function in the parent class • That is, when accessing an object as an instance of its parent class, only have access to the interface of parent Ex. class Polygon { protected: int width, height; public: Polygon(int w, int h): width(w), height(h) {} virtual int area() {return 0;} }; class Triangle: public Polygon { public: int area() { return (width * height / 2); } }; class Rectangle: public Polygon { public: int area() { return width * height; } }; // if we do not include the virtual function, // then we cannot do Triangle mytriangle(4, 6); Rectangle myrectangle(4, 6); Polygon mypolygons[2]; mypolygons[0] = mytriangle; mypolygons[1] = myrectangle; for (i=0; i < 2; i++) { cout << mypolygons[i].area(); }
Static variables in C++
• Initialized only once • Shared by all instances of a class • There is not a distinction between class and static variables in C++, as there is in Python • Static variables cannot be initialized using constructors • Defined using the static keyword Ex. Class Dummy { public: static int n; Dummy() { n++; } }; int Dummy::n = 0;
What are the invoker, command, and receiver in the following diagram?
• Invoker: button/menuitem/shortcut • Command: SaveCommand • Receiver: computer hard drive
Iterators in Python
• Iterable class must implement the __iter__ method • __iter__ returns an iterator object • The iterator must implement the __next__ method; __next__ must raise a StopIteration exception when it is done iterating Ex. class CapitalIterable: def __init__(self, string): self.string = string def __iter___(self): return CaptialIterator(self.string) class CapitalIterator: def __init__(self, string): self.words = [w.capitalize() for i in string.split()] self.index = 0 # __next__ takes a single argument def __next__(self): if self.index == len(self.words): raise StopIteration() word = self.words[self.index] seld.index += 1 return word a = CapitalIterable("hello world") # initializes an iterator, calls __next__, and handles the StopIteration exception automatically for x in a: print(x) # can manually access an iterator object iterator = inter(a) while True: try: print(next(iterator)) except StopIteration: break # notice __iter__ and __next__ are magic methods
References in C++
• Like a const pointer • Unlike a pointer, does not need to be dereferenced to use that value Ex. int p = 5; pointer: int* myint = &p; (*myint)++; reference: int& myint = p; myint++;
Abstract factory pattern
• Like the factory pattern, but there are multiple factory methods defined on a single object • Various abstract product classes, each of which defines an interface that subclasses must implement Ex. class chair: @property @abstractmethod def wood(self): return @property @abstractmethod def style(self): return • The abstract factory interface (an ABC) defines the factory methods for creating each of the abstract products • Each concrete factory must override these factory methods to return the particular subclasses of products
What are some use cases for the singleton pattern?
• Logging objects (access a single [global] instance) • Application configuration • Objects with no internal state (as in the strategy or state patterns) • Objects with read-only state
Adapter UML (composition)
• Note: the adapter composes the adaptee (appears the other way around in the UML class diagram)
Inheritance in C++
• Only public and protected attributes, methods are inherited from the parent class • Indicate that a class inherits from a parent class with the : operator • Public, protected, and private inheritance • In public inheritance, public attributes/methods are inherited as public, protected attributes/methods are inherited as protected, private attributes/methods are not inherited • In protected inheritance, public and protected attributes/methods are inherited as protected, ... Ex. class Polygon { protected: int width, height; public: Polygon(int w, int h): width(w), height(h){}; }; class Rectangle: public Polygon { public: int area() { return width*height; } };
Why learn design patterns?
• Speed up development • Less likely you will need to refactor your code • Makes code more readable to others who know the pattern • Build up vocabulary to communicate with other developers/managers
What is the difference between the state and strategy patterns?
• Strategy pattern is used to choose an algorithm at runtime (typically only one algorithm will be chosen for a particular use case), whereas state pattern allows for switching between different states dynamically • In code, strategy objects are not aware of one another; state objects need to know which other states they can switch to
Composite pattern
• Structural pattern • Building trees through object references • Polymorphism allows individual objects (leaves) and collection objects (nodes) to be treated as one type (more important for statically-typed languages ex. C++) • Identical UML diagram to the decorator pattern • The component interface is an ABC that defines operations common to both the leaf and composite objects; includes an execute method • The leaf class is a basic element of a tree with no subelements, the composite class aggregates other components (either composite or leaf instances) • The composite class typically also defines add, remove, and get_children methods • The composite class does not know the subclasses of its children • The composite delegates work to its sub-elements (with execute), processes intermediate results, and then returns the final result to the client
Adapter pattern
• Structural pattern • Converts an interface into another interface • Allows existing objects to work together without modifying their rigid public interfaces • A wrapper pattern, like the decorator, but the decorator does not change the object's interface (enhances an object without changing its interface) • Does change the adapter object's interface • Can be implemented using either multiple inheritance or composition Ex. (with multiple inheritance) class Target: def request(self) -> str: return "Target: The default target's behavior." class Adaptee: def specific_request(self) -> str: return ".eetpadA eht fo roivaheb laicepS" class Adapter(Target, Adaptee): # NOTICE: the adapter implements the same interface as its target def request(self) -> str: return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}" • In C++ we inherit publicly from the target, but privately from the adaptee Ex. class RectangleAdapter: public Rectangle, private LegacyRectangle **see LegacyRectangle example in C++
Flyweight pattern
• Structural pattern • When creating hundreds or thousands of separate objects, we are using a lot of memory for identical data • The flyweight pattern saves memory by keeping the shared state in a single object • The intrinsic state is independent of the context, is shared in the flyweight object • The behavior of the original object usually remains in the flyweight class; if in the context class, the flyweight just serves as a data object • The extrinsic state is specific to the context; is passed to the flyweight object when calling methods • The flyweight factory manages a pool of existing flyweights; the client passes intrinsic data to the flyweight factory, which either returns an existing flyweight or creates a new flyweight • The flyweight pattern is meant for memory optimization • Good in certain scenarios, but overkill for others (only necessary if creating many object instances) • Tradeoff: less memory, more complexity
Constant variables
• The const keyword specifies that a variable's value is constant • References the type on the left, except when const is the leftmost expression, in which case it references the type on the right Ex. const int c = 5; is the same as int const c = 5; • Pointer to a constant value: const int* p; • Constant pointer: int* const p = &x; • Constant reference: const int& param; (In the above example, we cannot do something like 'param = 5;')
Why in the factory and abstract factory patterns must all products implement the same abstract interface?
• This way, the client code does not depend on the specific variant of product returned from a factory (i.e. code is not coupled with the product) Ex. not if isinstance(product, Apple): product.munch() if isinstance(product, Popsicle): product.lick() instead # all food products have a common interface product.eat() • This way, client can work with any concrete factory and product
Constructor/Initializer in C++
• Unlike Python, C++ does not have a separate constructor and initializer • The C++ initializer has name matching the class name • The constructor does not have a return type • Can overload the constructor with different parameters • An alternative to *args, **kwargs in Python • Can initialize instance variables in the constructor body or the initializer list Ex. class Construct { public: float area; // Constructor with no parameters Construct() { area = 0; } // Constructor with two parameters Construct(int a, int b) { area = a * b; } }; int main() { // Calls the first constructor Construct obj1(); // Calls the second constructor Construct obj1(4, 5); }
Copy constructors C++
• Used automatically when assigning an object to another of the same type • Takes the form ClassName(const ClassName& old_obj); • Uses an implicit copy constructor if not defined; however, this constructor may not make a deep copy (unreliable) Ex. class Example { string* ptr; public: Example (const Example& x) : ptr(new string(x.content())) {} }
Generators
• Used if one would like to process a sequence without pulling a new list, set, or dictionary into memory i.e. don't need a complete container of objects over which we will iterate (ex. a 2 TB log file) • Can be created by wrapping a list comprehension in parentheses Ex. # a generator, NOT a tuple # notice: infile is a list warnings = (l for l in infile if 'WARNING' in l) for l in warnings: outfile.write(l) • The key to generator objects is the yield keyword • yield is similar to a return statement in the sense that it exits the function and returns a line; • Unlike return, though, when the function is called again via next(), it will start where it left off (the statement immediately after yield) Ex. def collatz(n): while n != 1: if not n % 2: n = n // 2 else: n = 3*n + 1 yield n • What is actually going on with generators? Python wraps the function in an iterable object which defines both __iter__ and __next__; whenever next() is called, the generator runs the function until it finds a yield statement
yield from keyword
• Used to yield data from an iterable object (ex. another generator, a list) Ex. yield from ( l.replace('\tWARNING', '') for l in infile if 'WARNING' in l )
Facade UML (example)
• Uses aggregation and/or composition in the facade wrapper class
List comprehensions
• Uses the iterator pattern • Much faster than standard for-loops when iterating over a large number of items; however, often not as readable • Any object that implements __iter__ can be the input to a list comprehension • Has the basic format [{method or function}i for i in myobjext if {conditional}] Ex. [i.capitalize() for i in "hello word" if i > 'h'] >> ['L', 'L', 'O', 'W', 'O', 'R']
Why is the singleton pattern problematic?
• Very situational • Why should x be made global in the first place? • Troublesome for concurrent code • Maybe the code should allow for multiple instances some day • Could pass around an object in method parameters rather than making it global
An alternative to the decorator pattern is ______.
• [Multiple] inheritance • Monkey patching (create an object's interface at runtime) • If there are many different decorators, multiple inheritance becomes infeasible; need to call parent methods with super (unpredictable in which order the parent methods will be called) • Decorators can be dynamically added to objects at runtime, multiple inheritance is permanent
Constant methods and objects
• const methods cannot be used to alter an object's attributes • This is because the 'this' pointer passed to a const method is a pointer to a const object Ex. // const keyword comes after the method int get_month() const; • Constant object instances can only be modified by their constructor and destructor Ex. const Loan myloan(100); // cannot do myloan.lower_amount(10) • Const methods can always be called (on both const and non-const objects) • Const methods cannot call a non-const method • Non-const methods cannot be called on a const object