iamor.me

Writings about Python, Go, General Tech, and Sounding Knowledgable

So, you already know how to code. Maybe it's not what you do all day - or maybe it is what you do all day. But, you get it: variables, functions, loops, etc etc. Plus… you're busy, but you still want to learn Go. Then, maybe, this is for you.

You've heard about Go… everyone has heard about Go. You know that it's crowning achievement is that it was both created by Google and not (yet) cancelled by Google. Which is probably good, since it powers some of the most important containerization software out there (Docker, Kubernetes).

This series won't go so deep that you'll be able to write you're own containerization software, but there's a strong possibility that you'll confidently write you're first Hello World program in 4–6 weeks (kidding).

Honestly, my goal is to break down the basic elements of Golang. I'll touch on the important stuff that, hopefully, will help you get started. Just remember that when I say bite-sized, I mean bite-sized. These are short reads. With quick visuals of code blocks that, I'm hoping, make it possible to read quickly and still come away with something.


Why Go

Go's founding goal was to simplify software development at Google. Unlike the languages it sought replace (C++, Java, Python), Go was meant to be fitter, happier, and more productive. Official word is that the goal was reasonably met. Go is simple and incredibly powerful. It's an excellent language that combines Python-like readability with C++-like capability.

By far, Go's best feature is the go fmt command: It takes whatever junk you just wrote and formats it the Go way. Whether you use VSCode, Zed, or GoLand, this should happen automatically. If you use Vim, figure it out: that's why you use Vim.

To move forward, you have to install Go. It's easy! Download it at go.dev.

1: Your First Go File

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Pretty simple, right? We've all the basics here: package main defines this file to be part of the main package. If you're writing a program that will be executed independently (i.e. by compiling a binary and running it on the command line), then this is where execution will start. If you're writing a library (something someone else can import into their program), I can't think of a reason why you would need a package main. More on that later.

import "fmt"

Imports are handled after the package declaration. Every go source file declares the imports needed for that source file. import "fmt" tells Go to import the fmt package - part of the standard library. The fmt package mainly handles inputs and outputs, like printing a line on the screen. Go doesn't like it when you import packages and don't use them. The Go formatter go fmt will, with a single exception, always remove unused imports.

func main() {
    fmt.Println("Hello, World!")
}

Our main function is where code execution will begin, but only if its part of package main. If you write a func main() in a package named math, then it won't be executed automatically. You'd have to call it some other way.

fmt.Println("Hello, World!")

For our Hello World print out, we reference the package fmt and use the Println function in that package. Println only requirement is the string to print on the screen: Hello, World!

Also note the curly braces {}. Functions, if-statements, and for- loops are grouped using the curlies. I really appreciate this. My pinky's fingernail doesn't, but I do.

func main() {
  if 1 == 1 {
    if 2 == 2 {
      if 3 == 3 {
        if 4 == 4 {
          if 5 == 5 {
            fmt.Println("It's true!")
          }
        }
      }
    }
  }
}

For the Python folks, you'll love the clarity that the {} braces provide. However, you'll hate it when your function ends with six closing braces.

That's it - I really meant it when I said bite-sized (and this was a little longer than I intended). Next time, we'll take a short look at functions.

Running Go Code

Start by creating a new directory named “go-bites-1” (mkdir go-bites-1) and then creating a new main.go file, either with your code editor or with the touch main.go command.

Copy and paste the top most code block in this article into your new main.go file and then run it from the command line by typing go run main.go. You'll see the following output:

$ go run main.go

Hello, World!

Extra Credit

You can override the name of an imported package if it interferes with your own functions or module names (or some other conflict comes up). Just add the new name before the import.

package main

import theformatpackage "fmt"

func main() {
    theformatpackage.Println("Hello, World!")
}

When importing multiple packages, remember to surround them with parenthesis. The import keyword can only be used once!

package main

import (
    "os"
    theformatpackage "fmt"
)

func main() {
    theformatpackage.Println("Hello, World!")
    os.Exit(0)
}

See the open-source s3 tool, s3packer, derived from the projects I mention in my posts: https://www.github.com/orme292/s3packer

Follow the series with the repo: go-bites @ github


See something wrong? Email [email protected].

ChatGPT is still relatively new, but everyone who might read this will already know how much of an effect it has had in nearly every industry. It's impossible to ignore right now. And, in fact, ignoring it might actually be a bad decision in the long run.

Of course, I'm saying this after having ignored it for the better part of a year. Once Microsoft released Bing chat, I did play around with it for a bit, though, I only treated it as a novelty. It felt similar to when Apple began integrating Siri into the iPhone. I would throw requests at it, get a laugh, raise an eyebrow, and pretty quickly make an assessment of it's full usefulness.

I came to realize that my initial response, in some part, was driven by fear — suddenly, I had felt more replaceable that ever. While at the same time, I recognized that ChatGPT wasn't in a position to replace most of us just yet, even though IBM's hopes are high. So, the months went on and I basically stopped using it.

Almost a year later, two projects I was working on called for leveraging object storage in a unique way. The available tools I came across didn't meet my needs, so I decided to write my own. Since I'd never used the AWS SDK for Go or the OCI SDK for Go, this seemed like the perfect opportunity to keep pushing forward with Go and get familiar with these monster SDKs.

Since the projects weren't for my own personal growth, I had a deadline. I'd need to jam a lot of knowledge into my head, quickly. The obvious first step was to hit google and look for examples on how to upload to S3 with the SDKs. There are, surprisingly, not many thorough examples of how to do this. I had 500 word Medium.com articles without context and two hour YouTube videos with ultra specific contexts. And, I had the AWS documentation which, at first, looks cryptic. As a whole these resources are helpful, but I needed a base to help get up and running. A finished product would need to authenticate to each service, walk directories, pull lists of files, name them to create a pseudo-directory structure, deal with potential overwrites, actually upload the files, handle hash validation, and more. The simple problem suddenly looked much bigger.

I made some progress as the hours and days went by, though inevitably I had to deal with more and more edge cases. Finally I remembered that GitHub Copilot had a free trial available — and now seemed like the best time to take advantage of it.

With Copilot, I was, almost immediately, off to the races. Copilot was suggesting code with astounding accuracy. In the moment, my thought process was “Well, this is okay because I already “knew” what I was going to type.” But, as the program grew from a small script, to a program with multiple modules and thousands of lines, I found myself using Copilot more liberally. It started to feel like cheating in a way. Copilot was feeding me these line completions; At most, I was glancing at them and sometimes just assuming they would work.

As my scope increased, and my project's complexity grew, the code completions became a distraction. I was trying to work around edge cases and error handling patterns that Copilot wasn't reliably picking up. Often times, the suggestions were way off base. I'd accidentally accept a completion, here and there, that would throw me off base and confuse me. Still, there are those times that Copilot would give me a real winner that would save me some precious time.

Consuming AI vs. Using AI Responsibly.

This article isn't about anything more than my own response to the rapid proliferation of ChatGPT and “AI”. I think the hype machine has been working overtime on this one. In fact, using the term AI is at best, completely inaccurate. Truth is, that GitHub's Copilot isn't thinking about solving the problems I'm trying to solve. It has zero sense of urgency or a need to responsibly complete a task. Copilot's LLM is pouring through all of it's training data it has and suggesting that I solve my own problems using a smattering of data provided by others.

Overtime, as all of our service providers hand our data away, these services will have more and more context and, potentially, the answers provided by the LLMs will be better and better. The other side of the coin is that the data being fed to the LLM could, overtime, reduce in quality as more and more of the incoming data is data that was generated by itself, or other LLMs — with all of the thoughtlessness and flaws intact. That's extremely cynical though, who knows.

As far as my own work is concerned, I've turned off the auto-complete options and switched over to Jetbrain's “AI” Assistant. I use it purely in a conversational way asking it questions, reading answers carefully, testing the replies, and then looking for confirmation on my own. It's usefulness has ballooned as I've developed a responsible pattern of use.

I know that responsible might be a big word to throw around, especially when it comes to “AI”, but in my own world, responsible simply means that I'm not doing myself a disservice by accepting any code as a given, worry-free, factual solution. Like in almost every other aspect of life, the data I have incoming, with a relative amount of effort, should be vetted and independently verified before I put it together and spew it out into the world. /s


See something wrong? Email [email protected].

Lots of news around Terraform lately. This could be THE time to get involved and up your Terraform game. Or, it might be a total waste of time.

Terraform

I've been spending a lot of time with Terraform over the last couple years. I've become generally familiar with it, but the truth is that I've mainly been spending that time modifying code (infrastructure) that was already built and deployed. I'm adding new IAM policies here, a couple new metric there, a few variables — but, with job scope shifts on the horizon, I've reached a point that made me want to have a solid base of knowledge from which to work. Notably, I've been using a proprietary internal provider that just doesn't translate to real world AWS or GCP (etc etc) experience.

I've talked about this before — all the time I spend googling answers (or searching slack and checking confluence) definitely get's the job done, but I lose out on all the benefits that come from really knowing something. And sometimes, you're in an interview and they ask you a question ... and realize that you can't answer because you never truly worked it out in your own head. And boy do they know as you stumble over your words trying not to sound like an idiot.

Anyway, it's a weird time to invest in Terraform. Hashicorp has moved Terraform from an open-source license to a more restricted BSL license. This won't affect my job (for now) since we're only using a custom provider, but this could be a reason to shift away from Terraform over the next months/years. Once I had heard the news, I immediately started looking for Terraform alternatives... almost instinctually. But, the landscape has changed so much since the end of the pandemic. This might be the time for Hashicorp to do this. It is, after all, entirely reasonable for a company to seek profit. I just hope they're ready for the competition.

The Terraform Associate 003 exam

I have a weird relationship with these certifications. I really don't expect them to change my life or open brand new doors that lead to giant stacks of cash. But, I can't deny that they're a great way build a thorough knowledge base.

Sure, they only ask you 60 questions and you have to wade through a hundred pages in a library of information to secure a certified terraform engineer “badge”. But, the benefit is actually hidden right there in all that information that you wade through. You might not need to know it all, but it's not going to hurt you.

The good news about the exam is that it focuses primarily on practical knowledge of the actual language, and I think you can pass just knowing the ins and out of HCL.

Terraform is all about modularization and reusing code, both in the real world and in the exam. If you're using it for your own projects you might not have invested time in writing modules, but I can tell you that they're essential as your infrastructure expands. Make sure you know how to take advantage of them. Write a couple and use a couple yourself.

Use a few different cloud providers and their terraform providers. AWS is the defacto place to start, but try OCI if you want a challenge.

Get to know the Terraform CLI. Not much to it, but use it to generate a few plans and an apply or two. Helps to know about validate and fmt as well.

Be sure to know about dynamic blocks and the lock file. Dynamic blocks + for loops are a power house in big terraform repos.

Finally, it's only $70 so maybe you can convince your boss to reimburse it if you pass.

The bad news is that Hashicorp uses it somewhat as a marketing platform to push their own tools like Terraform Cloud and Sentinel. Don't get me wrong, they're neat tools that I wouldn't have known about had I not required myself to pass this test, but knowing about them is less likely to help you in the real world.

Hashicorp Developer Terraform Cloud The Future of Terraform Must Be Open HashiCorp Terraform Associate Certification (003): All You Need to Know About


See something wrong? Email [email protected].

#programming #go #golang #composition #oop

Now that I've got a solid foundation in Python, plus 5-10 years of thinking I knew it already, I've jumped back over to Golang. Despite how popular Python is, I still can't help but want to spend time with Go. It's mainly just a feeling, but I love this language. I'll try and outline a few reasons why I always go back to Go.

It's Compiled

Golang is the first compiled language I've used to any real extent. Having spent so much time with interpreted languages like Python (and PHP / Ruby years ago), using a language that needs to be compiled brings freedom. With Go, I find that I spend way less time trying to manage environments and packages during development, and none at all if I ever need to distribute the compiled program. Maybe that's inexperience talking, but for now it's totally true.

Uniform Style + gofmt

package main
import ("fmt")

func main() 
{ 
    fmt.Println("Hello World!") 
} 

The official formatting style is an intentional no-brainer — the built ingo fmt (gofmt) tool implements a uniform style across all your code. “Formatting issues are the most contentious but the least consequential” says (Effective Go) and I can't agree more. The less time I need to spend worrying about formatting and style conventions, the better.

I remember submitting my first PR with tabs instead of spaces at an old job (in Perl or Coldfusion or something). It was kicked back to me almost immediately and it, still to this day, annoys the hell out of me. I get the whole debate and understand that uniformity is important — but come on. Python may have PEP8 formatters, but the fact that go has one built into the SDK is a real advantage on the style front.

It's Mostly Simple

Go was designed to solve problems pragmatically. You can pick up Go and implement a workable solution to a simple problem in very little time. I did it when I was first learning Go by making a wrapper around the shutdown command, so I could watch Youtube and not worry about my laptop if I fell asleep.

That's not to say that there's nothing difficult to grasp about Go — quite the opposite, especially for those of us without any real C/C++ experience. One of the biggest challenges I had was managing types — especially slices, arrays, and maps.

I've been practicing Go and Python with CodeWars and I'm noticing a glaring difference between the two. When I solve problems with Go, my solutions are typically compact and compare similarly to the ones submitted by others. When I solve problems in Python, more often than not, my solutions are longer than others — there's a heap of built-in functionality in Python that I just don't know about. There are list comprehensions all over the place, which... I'm pretty sure are just for show — most of the time they're just plain unreadable. I guess it could be the sheer number of Python programmers out there and that it's more mature... but I think it speaks to Go's nature.

Objected-Oriented with out being Object-Oriented

Go had been described as a post-OOP programing language. Despite having no official class support, you can still implement object oriented patterns into your project. Go takes a different approach by using interfaces, structs, methods, pointers, etc. Admittedly, these are things I struggle with the most — especially pointers.

Even though the learning curve is steep for me, I see the decision to not implement classes in Go results in a less constrained and less limited environment. Though, I can't argue with the ease and simplicity that comes from using classes, especially in Python.

___

Go Interfaces, Methods, Types, and Composition

I'll try to outline the basic idea behind composition and how Go lets you to solve problems with object-oriented thinking, without being conventionally object-oriented.

So, say an insurance company has a yard full of cars and their adjusters needs to generate a list of vehicles that need repair. At it's most basic, we need a Car type that will store information about each car, and then a function that will tell us whether each car needs to be repaired.

package main

import "fmt"

type InsuranceAdjuster interface {
	needsRepair() string
}

type Car struct {
	brand, model string
	runs         bool
}

func (c Car) needsRepair() string {
	if c.runs == false {
		return "needs repair"
	}
	return "runs fine"
}

func main() {

	car1 := Car{"Toyota", "Camry", true}
	car2 := Car{"Chevy", "Impala", false}

	fmt.Println(car1.brand, car1.model, car1.needsRepair())
	fmt.Println(car2.brand, car2.model, car2.needsRepair())
}

Run in Go Playground

Here, I create a new type named Car and gave it a few definable fields: like Brand, Model, and a boolean for whether it runs. Below it, I create a method needsRepair() that takes in the new Car type and returns a text string that says whether it runs or not.

I also created an interface called InsuranceAdjuster with a single requirement: to implement the interface, the type must have a method called needsRepair(). And since Car has a needsRepair() method, it implements the InsuranceAdjuster interface. What's the point of the interface? In this example, there's really no point. Everything would still work correctly with or without the interface.

But, say the insurance adjusters need to track trucks separately — maybe because they need to know the truck bed length. With Go, it's fairly easy — we can expand the program by implementing a Truck type.

type Truck struct {
	brand, model string
	bed          float32
	runs         bool
}

We've got our Truck type now, but now our text string doesn't say anything about the bed length. This is where the interface comes in handy.

package main

import (
	"fmt"
)

type InsuranceAdjuster interface {
	needsRepair() string
}

type Car struct {
	brand, model string
	runs         bool
}

type Truck struct {
	brand, model string
	bed          float32
	runs         bool
}

func (c Car) needsRepair() string {
	if c.runs == false {
		return fmt.Sprintf("%s %s needs repair", c.brand, c.model)
	}
	return fmt.Sprintf("%s %s runs fine", c.brand, c.model)
}

func (t Truck) needsRepair() string {
	if t.runs == false {
		return fmt.Sprintf("%s %s with a %.1f foot bed needs repair", t.brand, t.model, t.bed)
	}
	return fmt.Sprintf("%s %s with a %.1f foot bed runs fine", t.brand, t.model, t.bed)
}

func main() {

	car1 := Car{"Toyota", "Camry", true}
	car2 := Car{"Chevy", "Impala", false}
	truck1 := Truck{
		brand: "Toyota",
		model: "Tacoma",
		bed:   5.5,
		runs:  true,
	}
	truck2 := Truck{
		brand: "Nissan",
		model: "Frontier",
		bed:   4.9,
		runs:  false,
	}

	claims := []InsuranceAdjuster{car1, car2, truck1, truck2}
	for _, claims := range claims {
		fmt.Println(claims.needsRepair())
	}
}

Run in Go Playground

Now we've got cars and trucks tracked separately and the interface has a purpose now (kind of). At the bottom, we create a new claims variable and make it an instance of the InsuranceAdjuster interface, and we throw all the vehicles into it. Now we can treat them all the same and when we run needsRepair(), we get the correct output depending on whether it's a truck or a car.

Now, say they want to track the type of damage that each vehicle has — body or engine. We can create a new type called Damage, and then require that each vehicle type uses it.

type Damage struct {
	engine bool
	body   bool
}

type Car struct {
	brand, model string
	damage       Damage
	runs         bool
}

type Truck struct {
	brand, model string
	bed          float32
	damage       Damage
	runs         bool
}

Now, we'll adjust needsRepair() to describe the damage. We end up with this:

package main

import (
	"fmt"
)

type InsuranceAdjuster interface {
	needsRepair() string
}

type Damage struct {
	engine bool
	body   bool
}

type Car struct {
	brand, model string
	damage       Damage
	runs         bool
}

type Truck struct {
	brand, model string
	bed          float32
	damage       Damage
	runs         bool
}

func (c Car) needsRepair() string {
	if c.runs == false {
		return fmt.Sprintf("%s %s needs repair. \n\tBody damage: %t, \n\tEngine damage: %t.", c.brand, c.model, c.damage.body, c.damage.engine)
	}
	return fmt.Sprintf("%s %s runs fine. \n\tBody damage: %t, \n\tEngine damage: %t.", c.brand, c.model, c.damage.body, c.damage.engine)
}

func (t Truck) needsRepair() string {
	if t.runs == false {
		return fmt.Sprintf("%s %s with a %.1f foot bed needs repair. \n\tBody damage: %t, \n\tEngine damage: %t.", t.brand, t.model, t.bed, t.damage.body, t.damage.engine)
	}
	return fmt.Sprintf("%s %s with a %.1f foot bed runs fine. \n\tBody damage: %t, \n\tEngine damage: %t.", t.brand, t.model, t.bed, t.damage.body, t.damage.engine)
}

func main() {

	car1 := Car{
		brand: "Toyota",
		model: "Camry",
		damage: Damage{
			body:   true,
			engine: false,
		},
		runs: true,
	}
	car2 := Car{
		brand: "Chevy",
		model: "Impala",
		damage: Damage{
			body:   false,
			engine: true,
		},
		runs: false,
	}
	truck1 := Truck{
		brand: "Toyota",
		model: "Tacoma",
		bed:   5.5,
		damage: Damage{
			body:   true,
			engine: false,
		},
		runs: true,
	}
	truck2 := Truck{
		brand: "Nissan",
		model: "Frontier",
		bed:   4.9,
		damage: Damage{
			body:   false,
			engine: true,
		},
		runs: false,
	}

	claims := []InsuranceAdjuster{car1, car2, truck1, truck2}
	for _, claims := range claims {
		fmt.Println(claims.needsRepair())
	}
}

Run in Go Playground

Sure, this isn't the best example, and my outline kind of assumes you already know a bit about Go, but it still shows how interfaces and composition can work in Go. And, in defense of the sheer number of lines this small program took — keep in mind that this was all formatted for readability.


See something wrong? Email [email protected].

#programming #python #certifications

Over the last couple weeks, I've been studying to take the Python Institute's PCEP exam — it's the most basic test they offer and ran me about $60. I bought the voucher on a whim — but I bought it on a whim for a good reason.

I have a tendency to think myself into inaction. I spend too much time considering all the negative aspects of something and as a result, I fail to do anything. The result of which keeps me on a trajectory to sameness. Right now, that doesn't work, because the trajectory I'm on sucks. I've been here before and I've done things about it — that's how my brain knew to buy it before I could talk myself out of it.

Now comes the part where I have to come to terms with either the value or lack of value that it provides. Generally, I'm a proponent of certifications — they helped me get my first tech job and even helped me get promotions. But word around the internet is that a certification for a programming language doesn't mean much — it's the things you make and your contributions that matter. They're even saying that I should get a CS degree instead of wasting my time with this stuff.

This is exactly why I need to put this certification in it's place. I already know that the PCEP cert isn't going to land me a new or better job. The certification's value isn't the certification itself. Even the Python Institute, the ones trying to convince people to buy the certs, list the main benefits of getting a certification to be:

  • Increased confidence
  • Greater job satisfaction
  • Increased quality and value of work contributions
  • Increased respect from peers
  • Ability to perform a new task or fill a new role

Nothing on this list says “Land a High Paying Job”.

Really though, this is a great list — but there's only one thing on the list that matters. The increased confidence. All the other things on the list will come easier as a result of that.

The last time around, my certifications never resulted in a meaningful confidence boost. I suffered from the trending “imposter syndrome”. That's a bigger issue, that, I think, stems from my own history. As a kid, I always picked up on things quickly so I never really had to study. As I grew up, this became less and less true. By the time I got to college, things weren't clicking and I just didn't have the desire or fortitude to push through with anything. That mentality stuck with me, though, even as I pursued a career in tech. And even though I would pass these tests, I still felt like I didn't know anything. Maybe it's time and experience, but it feels different now.

This test really just lays onto me a requirement to study the material and really learn it. I've always wanted to be knowledgable enough to create, but I never had the confidence to admit it. So, ultimately this certificate will hold a significant value for me, personally.


See something wrong? Email [email protected].

#programming #python

It's hard for me to write about the basics of programming in Python for a couple reasons. First, I'm old — or, older than the average programmer according to big media. Second, I've been using Python for close to a decade. It feels a little immature and regressive, but all things considered, it's necessary. At least, so I feel more knowledgable when asked a question.

Initially, I was excited to talk about bitwise operators and binary numbers, but, as I dove deeper it became clear that the world of binary is quite complex. Since we're talking about Python, I can at least narrow the focus down to that. However, If you want to read up on signed and unsigned integers, binary representation, and the math behind the operand, there are plenty of resources. I'll list a couple at the bottom of the article. For now, know that the info below is specific to Python and it's just scratching the surface of a wider field.

Separately, It's hard to think of a future in which I'd have to use any of Python's bitwise operators, but I still found them to be one of the most interesting parts of the entry-level Python Certification. And, after all, there are real-world problems that can be solved with bitwise ops — compression, encryption, network communications, and I'm sure you could find a use for them in the IoT world. I probably won't be the one solving these problems, but someone is/will. Really though, I'm not even sure that Python is the best tool to use to solve these problems, but that's something for someone else to deal with.

In reality, these operators are all performing a basic, non-complex, function. Conceptually, most of them operate in a similar manner as their identically named logical counterparts — the difference being that they compare individual bits. Unknowingly, at first glance, you might expect 0b100100 & 0b101010 to equal True or False, and that's okay. But, you'd be wrong.

I'll go over four of Python's bitwise operators and the shift operators by outlining their basic functions.

The operators are: & AND | OR ^ XOR (exclusive OR) ~ NOT << Left Shift >> Right Shift

Bitwise AND

The bitwise AND operator & performs logical conjunction on the bit pairs of both operands. For each pair of bits at the same position, a one is returned when both bits are one (or when both bits are on). For example:

1 0 1 0 1 0 1
1 0 0 1 1 0 0
--------------
1 0 0 0 1 0 0
result = 0b1010101 & 0b1001100
print(bin(result)) # 0b1000100

See how we end up with a new binary number? Except now, we have 1 in the same position as each pair of bits that are both set to 1. For the pairs that are both 0, or only one bit is set to 1, they result in a 0.

Bitwise OR

The bitwise OR operator | performs logical disjunction. For each pair of bits, if at least one bit is set to 1, the result is 1. For example:

1 0 1 0 1 0 1
1 0 0 1 1 0 0
--------------
1 0 1 1 1 0 1
result = 0b1010101 | 0b1001100
print(bin(result)) # 0b1011101

The binary number returned now has a 1 at each position in which the corresponding pair of bits had at least one bit set to 1.

Bitwise XOR

The bitwise XOR, exclusive OR, operator ^operates similarly to OR | with a notable exception. When comparing two bits that are both set to 1, XOR will return 0, while AND and OR would both return 1. With XOR ^, each bit in a pair must be the opposite of the other. Two 1s don't make a 1.

1 0 1 0 1 0 1
1 0 0 1 1 0 0
--------------
0 0 1 1 0 0 1
result = 0b1010101 ^ 0b1001100
print(bin(result)) # 0b11001 (the leading 0s are dropped)

Each bit pair must be the opposite of the other, if they're the same then a 0 is returned.

Bitwise NOT

x = 5
y = ~x
print(y) # -6

The last bitwise operator NOT ~, in Python, returns the complement of the argument minus the number you get by switching all the bits to their opposite, or, -x - 1. There's a whole bunch of math and logic behind NOT and logical negation, but for now, know that NOT ~ is -x - 1.

Bitwise Left Shift

The left shift operator << moves all the bits of the first operand to the left by the number of positions specified in the second operand. To compensate for the shift, a 0 bit will be added on the opposite side.

x = 0b101
x <<= 2 # or x = x << 2
print(bin(x)) #0b10100

Visually, you'll see it operate like this: 0 b 1 0 1 << 2 = 0b10100.

Bitwise Right Shift

The right shift operator >> moves all the bits of the first operand to the (you guessed it) right by the number of positions specified in the second operand.

x = 0b101
x >>= 2 # or x = x >> 2
print(bin(x)) #0b1

If you try to shift more bits than the number has, you'll end up with 0 (or 0b0).

Visually, >> will operate like this: 0 b 1 0 1 >> 2 = 0b1.

Now that you've gotten to the end of my article, if you're interested in bitwise operators and how Unix file permissions take advantage of them, Alex Hyett has an article on just that topic.

For a real solid, thorough, explanation of Bitwise operators, Binary representation, and more, see this article from RealPython.


See something wrong? Email [email protected].

#programming #python

I've been using Python for years, so I always felt comfortable in saying that I'm a Python programmer. But, recently I was put in a position in which I had to actually talk about it... and I just couldn't do it. To me, this was a failure. It's made me reconsider my standing with all these technologies that I claim to be knowledgable with. While I felt like a fraud at the time, I know that the reality of the situation is much much different. I have plenty of experience with them, but my focus was always task focused, rather than knowledge focused. So, I'm thinking it's time to find some kind of balance between the two.

It didn't take much time or thought for me to realize that I've only ever really made one thing (for my own use) with Python — an API — and I never ended up using it. I've spent plenty of time creating Python programs to automate and complete job tasks, but I think the exploratory nature of building something on your own would be a good thing for me.

It's a great time for me to realize all this, because I'm already in the middle of rethinking whether I am where I want to be career-wise. So I made the decision to go for the entry-level Python certification from the Python Institute. Sure, it may be the easiest one they offer, but at least it will quickly give me an idea of where I stand.

I thought that I'd breeze right through the study course, so I skipped all the reading and went right to the quizzes and tests. I failed. But, I swallowed my pride, went back to the beginning, and started really going through the material. After a couple weeks, between work and life distractions, I finished the beginner course and, you know, I came away from it feeling more confident than I ever felt. Even with something so basic, it was good to know that I had the capacity to build a knowledge base.

I came across a few things in the course that I never took the time to understand, or just never committed to memory (I blame the internet for the later). The most notable of them being:

  • Tuples
  • Basic Math (there's a lot of it in the course)
  • Binary and Bitwise Operations

I can't imagine I'll ever really need to deal with binary numbers, let alone shifting bits, but it's still exciting anyway. I imagine that it'll be beneficial as I spend more time with Golang rather than with Python.

So, I wanted to post about bitwise operators (because it's always been a topic that I avoided). But, I can't do that without first figuring out binary numbers. You might know about converting base 10 and base 2 numbers, but I didn't. Plus, I'm hoping that by writing this out will, I'll be reinforcing the material (Really, why else would I be doing this).

I know you could just google something like “Binary converter”, which is what I had always done, but, today, I won't be doing that.

Converting an Integer to a Binary Number

A binary number is a number expressed in the base 2 numeral system. The base 2 system uses only two digits to represent any number: 0s and 1s. Every number in our base 10 system (0-9... get it?) can be represented in binary, or base 2, using only 0s and 1s.

To convert any base 10 number to binary, you divide it by 2 until you reach 0. The remainder from each step is your binary number (from right to left) It makes more sense when you see it happening.

Let's say you want to convert the number 12 to binary. You start with 12 and divide by 2, which gives you 6 with a remainder of 0.

12 ÷ 2 = 6 R 0

That remainder of 0 will be the right most digit in the binary representation of 12. Now we know that 12 in binary will be ? ? ? 0 (I'm showing you that it will be 4 digits, but normally you might now know).

Next, take that 6 from the previous step and divide it by 2 again.

6 ÷ 2 = 3 R 0

You get 3 with a remainder of 0. That 0 will be the next digit in our binary representation of 12, ? ? 0 0. It goes to the left of the previous digit.

Now we take that 3 from the previous step and, again, divide by 2.

3 ÷ 2 = 1 R 1

You'll get 1 with a remainder of 1. Our next binary digit will be 1, ? 1 0 0.

Finally, you can take the 1 from the previous step and divide by 2.

1 ÷ 2 = 0 R 1

We've got a quotient of 0, which means this is last step. Take the remainder and we've got our binary, base 2, representation of 12: 1100 ** Note that python will show it like this: 0b1100. **

print(bin(12)) # 0b1100

In summary, this is what we've done

12 ÷ 2 = 6 R 0
 6 ÷ 2 = 3 R 0
 3 ÷ 2 = 1 R 1
 1 ÷ 2 = 0 R 1

Easy, right? Let's try it with 19.

19 ÷ 2 = 9 R 1
 9 ÷ 2 = 4 R 1
 4 ÷ 2 = 2 R 0
 2 ÷ 2 = 1 R 0
 1 ÷ 2 = 0 R 1

It's a breeze. 0b10011!

Converting a Binary Number to a Base 10 Integer

Converting back to a base 10 number is more confusing. But it's still just basic math, which you'll realize once you see it.

Let's convert 1100 back into base 10, that way you'll know if you're doing it correctly (we know its going to be 12 from above).

The method we're going to use is called Positional Notation, though I'm not sure if that's official method name.

1100 has four positions that need to be noted. Start counting from the right with the first position being 0.

$1100$ Binary $3210$ Positional Notation

The final step is to take 2 to the power of each position, multiplied by the binary number in that position.

$1100$ Binary $3210$ Positional Notation $1(2^3)\;\;1(2^2)\;\;0(2^1)\;\;0(2^0)$

You're answer will be the sum of the above: $8+4+0+0=12$ Easy. But, just to make sure you have it figured out, try to convert 1001011 to a base 10 integer.

Start with positional notation: $1001011$ $6543210$

Lastly, solve for each position by taking 2 to the power of each position number, multiplied by the position's binary number. $1(2^6)\;0(2^5)\;0(2^4)\;1(2^3)\;0(2^2)\;1(2^1)\;1(2^0)$

Add them all together, and you'll get your answer: $64+0+0+8+0+2+1=75$


See something wrong? Email [email protected].